2023-10-03 14:23:56 +00:00
//===== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ======//
2020-04-22 16:56:21 +00:00
//
// Purpose:
//
2023-10-03 14:23:56 +00:00
//===========================================================================//
2020-04-22 16:56:21 +00:00
# include "cbase.h"
# include "mathlib/mathlib.h"
# include "util_shared.h"
# include "model_types.h"
# include "convar.h"
# include "IEffects.h"
# include "vphysics/object_hash.h"
# include "mathlib/IceKey.H"
# include "checksum_crc.h"
# include "particle_parse.h"
# include "KeyValues.h"
2023-10-03 14:23:56 +00:00
# include "icommandline.h"
2020-04-22 16:56:21 +00:00
# ifdef CLIENT_DLL
2023-10-03 14:23:56 +00:00
# include "clientleafsystem.h"
2020-04-22 16:56:21 +00:00
# include "c_te_effect_dispatch.h"
# else
# include "te_effect_dispatch.h"
bool NPC_CheckBrushExclude ( CBaseEntity * pEntity , CBaseEntity * pBrush ) ;
# endif
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
ConVar r_visualizetraces ( " r_visualizetraces " , " 0 " , FCVAR_CHEAT ) ;
2023-10-03 14:23:56 +00:00
ConVar developer ( " developer " , " 0 " , FCVAR_RELEASE , " Set developer message level " ) ; // developer mode
# ifdef DETECT_TRACE_SPIKES
float g_TraceSpikeTolerance = 0.25 ;
ConVar trace_spike_tolerance ( " trace_spike_tolerance " , " 0.25 " ) ;
void DoReportExpensiveTrace ( bool repeat , float time )
{
if ( g_TraceSpikeTolerance > 0.0f )
{
Msg ( " %s%f! \n " , ( repeat ) ? " R: " : " " , time ) ;
}
g_TraceSpikeTolerance = trace_spike_tolerance . GetFloat ( ) ;
}
# endif
2020-04-22 16:56:21 +00:00
float UTIL_VecToYaw ( const Vector & vec )
{
if ( vec . y = = 0 & & vec . x = = 0 )
return 0 ;
float yaw = atan2 ( vec . y , vec . x ) ;
yaw = RAD2DEG ( yaw ) ;
if ( yaw < 0 )
yaw + = 360 ;
return yaw ;
}
float UTIL_VecToPitch ( const Vector & vec )
{
2023-10-03 14:23:56 +00:00
float pitch = 0 ;
Vector tmp = vec ;
if ( VectorNormalize ( tmp ) > 0 )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
pitch = RAD2DEG ( asin ( - tmp . z ) ) ;
2020-04-22 16:56:21 +00:00
}
return pitch ;
}
float UTIL_VecToYaw ( const matrix3x4_t & matrix , const Vector & vec )
{
Vector tmp = vec ;
VectorNormalize ( tmp ) ;
float x = matrix [ 0 ] [ 0 ] * tmp . x + matrix [ 1 ] [ 0 ] * tmp . y + matrix [ 2 ] [ 0 ] * tmp . z ;
float y = matrix [ 0 ] [ 1 ] * tmp . x + matrix [ 1 ] [ 1 ] * tmp . y + matrix [ 2 ] [ 1 ] * tmp . z ;
if ( x = = 0.0f & & y = = 0.0f )
return 0.0f ;
float yaw = atan2 ( - y , x ) ;
yaw = RAD2DEG ( yaw ) ;
if ( yaw < 0 )
yaw + = 360 ;
return yaw ;
}
float UTIL_VecToPitch ( const matrix3x4_t & matrix , const Vector & vec )
{
2023-10-03 14:23:56 +00:00
float pitch = 0 ;
2020-04-22 16:56:21 +00:00
Vector tmp = vec ;
2023-10-03 14:23:56 +00:00
if ( VectorNormalize ( tmp ) > 0 )
{
float z = matrix [ 0 ] [ 2 ] * tmp . x + matrix [ 1 ] [ 2 ] * tmp . y + matrix [ 2 ] [ 2 ] * tmp . z ;
pitch = RAD2DEG ( asin ( - z ) ) ;
if ( pitch < 0 )
pitch + = 360 ;
}
2020-04-22 16:56:21 +00:00
return pitch ;
}
Vector UTIL_YawToVector ( float yaw )
{
Vector ret ;
ret . z = 0 ;
float angle = DEG2RAD ( yaw ) ;
SinCos ( angle , & ret . y , & ret . x ) ;
return ret ;
}
//-----------------------------------------------------------------------------
// Purpose: Helper function get get determinisitc random values for shared/prediction code
// Input : seedvalue -
// *module -
// line -
// Output : static int
//-----------------------------------------------------------------------------
static int SeedFileLineHash ( int seedvalue , const char * sharedname , int additionalSeed )
{
CRC32_t retval ;
CRC32_Init ( & retval ) ;
CRC32_ProcessBuffer ( & retval , ( void * ) & seedvalue , sizeof ( int ) ) ;
CRC32_ProcessBuffer ( & retval , ( void * ) & additionalSeed , sizeof ( int ) ) ;
CRC32_ProcessBuffer ( & retval , ( void * ) sharedname , Q_strlen ( sharedname ) ) ;
CRC32_Final ( & retval ) ;
return ( int ) ( retval ) ;
}
float SharedRandomFloat ( const char * sharedname , float flMinVal , float flMaxVal , int additionalSeed /*=0*/ )
{
Assert ( CBaseEntity : : GetPredictionRandomSeed ( ) ! = - 1 ) ;
int seed = SeedFileLineHash ( CBaseEntity : : GetPredictionRandomSeed ( ) , sharedname , additionalSeed ) ;
RandomSeed ( seed ) ;
return RandomFloat ( flMinVal , flMaxVal ) ;
}
int SharedRandomInt ( const char * sharedname , int iMinVal , int iMaxVal , int additionalSeed /*=0*/ )
{
Assert ( CBaseEntity : : GetPredictionRandomSeed ( ) ! = - 1 ) ;
int seed = SeedFileLineHash ( CBaseEntity : : GetPredictionRandomSeed ( ) , sharedname , additionalSeed ) ;
RandomSeed ( seed ) ;
return RandomInt ( iMinVal , iMaxVal ) ;
}
Vector SharedRandomVector ( const char * sharedname , float minVal , float maxVal , int additionalSeed /*=0*/ )
{
Assert ( CBaseEntity : : GetPredictionRandomSeed ( ) ! = - 1 ) ;
int seed = SeedFileLineHash ( CBaseEntity : : GetPredictionRandomSeed ( ) , sharedname , additionalSeed ) ;
RandomSeed ( seed ) ;
// HACK: Can't call RandomVector/Angle because it uses rand() not vstlib Random*() functions!
// Get a random vector.
2022-03-01 20:00:42 +00:00
Vector random ;
random . x = RandomFloat ( minVal , maxVal ) ;
random . y = RandomFloat ( minVal , maxVal ) ;
random . z = RandomFloat ( minVal , maxVal ) ;
return random ;
2020-04-22 16:56:21 +00:00
}
QAngle SharedRandomAngle ( const char * sharedname , float minVal , float maxVal , int additionalSeed /*=0*/ )
{
Assert ( CBaseEntity : : GetPredictionRandomSeed ( ) ! = - 1 ) ;
int seed = SeedFileLineHash ( CBaseEntity : : GetPredictionRandomSeed ( ) , sharedname , additionalSeed ) ;
RandomSeed ( seed ) ;
// HACK: Can't call RandomVector/Angle because it uses rand() not vstlib Random*() functions!
// Get a random vector.
2022-03-01 20:00:42 +00:00
Vector random ;
random . x = RandomFloat ( minVal , maxVal ) ;
random . y = RandomFloat ( minVal , maxVal ) ;
random . z = RandomFloat ( minVal , maxVal ) ;
return QAngle ( random . x , random . y , random . z ) ;
2020-04-22 16:56:21 +00:00
}
//-----------------------------------------------------------------------------
//
// Shared client/server trace filter code
//
//-----------------------------------------------------------------------------
bool PassServerEntityFilter ( const IHandleEntity * pTouch , const IHandleEntity * pPass )
{
if ( ! pPass )
return true ;
if ( pTouch = = pPass )
return false ;
const CBaseEntity * pEntTouch = EntityFromEntityHandle ( pTouch ) ;
const CBaseEntity * pEntPass = EntityFromEntityHandle ( pPass ) ;
if ( ! pEntTouch | | ! pEntPass )
return true ;
// don't clip against own missiles
if ( pEntTouch - > GetOwnerEntity ( ) = = pEntPass )
return false ;
// don't clip against owner
if ( pEntPass - > GetOwnerEntity ( ) = = pEntTouch )
return false ;
return true ;
}
//-----------------------------------------------------------------------------
// A standard filter to be applied to just about everything.
//-----------------------------------------------------------------------------
bool StandardFilterRules ( IHandleEntity * pHandleEntity , int fContentsMask )
{
CBaseEntity * pCollide = EntityFromEntityHandle ( pHandleEntity ) ;
// Static prop case...
if ( ! pCollide )
return true ;
SolidType_t solid = pCollide - > GetSolid ( ) ;
const model_t * pModel = pCollide - > GetModel ( ) ;
if ( ( modelinfo - > GetModelType ( pModel ) ! = mod_brush ) | | ( solid ! = SOLID_BSP & & solid ! = SOLID_VPHYSICS ) )
{
if ( ( fContentsMask & CONTENTS_MONSTER ) = = 0 )
return false ;
}
// This code is used to cull out tests against see-thru entities
2023-10-03 14:23:56 +00:00
if ( ! ( fContentsMask & CONTENTS_WINDOW ) )
{
# ifdef CLIENT_DLL
if ( g_pClientLeafSystem - > GetTranslucencyType ( pCollide - > RenderHandle ( ) ) = = RENDERABLE_IS_TRANSLUCENT )
return false ;
# else
bool bIsTranslucent = modelinfo - > IsTranslucent ( pModel ) ;
bool bIsTwoPass = modelinfo - > IsTranslucentTwoPass ( pModel ) ;
if ( bIsTranslucent & & ! bIsTwoPass )
return false ;
# endif
}
2020-04-22 16:56:21 +00:00
// FIXME: this is to skip BSP models that are entities that can be
// potentially moved/deleted, similar to a monster but doors don't seem to
// be flagged as monsters
// FIXME: the FL_WORLDBRUSH looked promising, but it needs to be set on
// everything that's actually a worldbrush and it currently isn't
if ( ! ( fContentsMask & CONTENTS_MOVEABLE ) & & ( pCollide - > GetMoveType ( ) = = MOVETYPE_PUSH ) ) // !(touch->flags & FL_WORLDBRUSH) )
return false ;
return true ;
}
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
//-----------------------------------------------------------------------------
// Simple trace filter
//-----------------------------------------------------------------------------
CTraceFilterSimple : : CTraceFilterSimple ( const IHandleEntity * passedict , int collisionGroup ,
2023-10-03 14:23:56 +00:00
ShouldHitFunc_t pExtraShouldHitFunc )
2020-04-22 16:56:21 +00:00
{
m_pPassEnt = passedict ;
m_collisionGroup = collisionGroup ;
m_pExtraShouldHitCheckFunction = pExtraShouldHitFunc ;
}
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
//-----------------------------------------------------------------------------
// The trace filter!
//-----------------------------------------------------------------------------
bool CTraceFilterSimple : : ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
if ( ! StandardFilterRules ( pHandleEntity , contentsMask ) )
return false ;
if ( m_pPassEnt )
{
if ( ! PassServerEntityFilter ( pHandleEntity , m_pPassEnt ) )
{
return false ;
}
}
// Don't test if the game code tells us we should ignore this collision...
CBaseEntity * pEntity = EntityFromEntityHandle ( pHandleEntity ) ;
if ( ! pEntity )
return false ;
if ( ! pEntity - > ShouldCollide ( m_collisionGroup , contentsMask ) )
return false ;
if ( pEntity & & ! g_pGameRules - > ShouldCollide ( m_collisionGroup , pEntity - > GetCollisionGroup ( ) ) )
return false ;
if ( m_pExtraShouldHitCheckFunction & &
2023-10-03 14:23:56 +00:00
( ! ( m_pExtraShouldHitCheckFunction ( pHandleEntity , contentsMask ) ) ) )
2020-04-22 16:56:21 +00:00
return false ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Trace filter that only hits NPCs and the player
//-----------------------------------------------------------------------------
bool CTraceFilterOnlyNPCsAndPlayer : : ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
if ( CTraceFilterSimple : : ShouldHitEntity ( pHandleEntity , contentsMask ) )
{
CBaseEntity * pEntity = EntityFromEntityHandle ( pHandleEntity ) ;
if ( ! pEntity )
return false ;
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
return ( pEntity - > IsNPC ( ) | | pEntity - > IsPlayer ( ) ) ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose: Trace filter that only hits anything but NPCs and the player
//-----------------------------------------------------------------------------
bool CTraceFilterNoNPCsOrPlayer : : ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
if ( CTraceFilterSimple : : ShouldHitEntity ( pHandleEntity , contentsMask ) )
{
CBaseEntity * pEntity = EntityFromEntityHandle ( pHandleEntity ) ;
if ( ! pEntity )
return NULL ;
# ifndef CLIENT_DLL
if ( pEntity - > Classify ( ) = = CLASS_PLAYER_ALLY )
return false ; // CS hostages are CLASS_PLAYER_ALLY but not IsNPC()
# endif
return ( ! pEntity - > IsNPC ( ) & & ! pEntity - > IsPlayer ( ) ) ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Trace filter that skips two entities
//-----------------------------------------------------------------------------
CTraceFilterSkipTwoEntities : : CTraceFilterSkipTwoEntities ( const IHandleEntity * passentity , const IHandleEntity * passentity2 , int collisionGroup ) :
BaseClass ( passentity , collisionGroup ) , m_pPassEnt2 ( passentity2 )
{
}
bool CTraceFilterSkipTwoEntities : : ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
Assert ( pHandleEntity ) ;
if ( ! PassServerEntityFilter ( pHandleEntity , m_pPassEnt2 ) )
return false ;
return BaseClass : : ShouldHitEntity ( pHandleEntity , contentsMask ) ;
}
//-----------------------------------------------------------------------------
// Trace filter that can take a list of entities to ignore
//-----------------------------------------------------------------------------
CTraceFilterSimpleList : : CTraceFilterSimpleList ( int collisionGroup ) :
CTraceFilterSimple ( NULL , collisionGroup )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTraceFilterSimpleList : : ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
if ( m_PassEntities . Find ( pHandleEntity ) ! = m_PassEntities . InvalidIndex ( ) )
return false ;
return CTraceFilterSimple : : ShouldHitEntity ( pHandleEntity , contentsMask ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Add an entity to my list of entities to ignore in the trace
//-----------------------------------------------------------------------------
void CTraceFilterSimpleList : : AddEntityToIgnore ( IHandleEntity * pEntity )
{
m_PassEntities . AddToTail ( pEntity ) ;
}
2023-10-03 14:23:56 +00:00
void CTraceFilterSimpleList : : AddEntitiesToIgnore ( int nCount , IHandleEntity * * ppEntities )
{
int nIndex = m_PassEntities . AddMultipleToTail ( nCount ) ;
memcpy ( & m_PassEntities [ nIndex ] , ppEntities , nCount * sizeof ( IHandleEntity * ) ) ;
}
//-----------------------------------------------------------------------------
// Trace filter that hits only the pass entity
//-----------------------------------------------------------------------------
CTraceFilterOnlyHitThis : : CTraceFilterOnlyHitThis ( const IHandleEntity * hitentity )
{
m_pHitEnt = hitentity ;
}
bool CTraceFilterOnlyHitThis : : ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
return m_pHitEnt = = pHandleEntity ;
}
2020-04-22 16:56:21 +00:00
//-----------------------------------------------------------------------------
// Purpose: Custom trace filter used for NPC LOS traces
//-----------------------------------------------------------------------------
CTraceFilterLOS : : CTraceFilterLOS ( IHandleEntity * pHandleEntity , int collisionGroup , IHandleEntity * pHandleEntity2 ) :
CTraceFilterSkipTwoEntities ( pHandleEntity , pHandleEntity2 , collisionGroup )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTraceFilterLOS : : ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
CBaseEntity * pEntity = EntityFromEntityHandle ( pHandleEntity ) ;
if ( ! pEntity - > BlocksLOS ( ) )
return false ;
return CTraceFilterSimple : : ShouldHitEntity ( pHandleEntity , contentsMask ) ;
}
//-----------------------------------------------------------------------------
// Trace filter that can take a classname to ignore
//-----------------------------------------------------------------------------
CTraceFilterSkipClassname : : CTraceFilterSkipClassname ( const IHandleEntity * passentity , const char * pchClassname , int collisionGroup ) :
CTraceFilterSimple ( passentity , collisionGroup ) , m_pchClassname ( pchClassname )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTraceFilterSkipClassname : : ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
CBaseEntity * pEntity = EntityFromEntityHandle ( pHandleEntity ) ;
if ( ! pEntity | | FClassnameIs ( pEntity , m_pchClassname ) )
return false ;
return CTraceFilterSimple : : ShouldHitEntity ( pHandleEntity , contentsMask ) ;
}
//-----------------------------------------------------------------------------
// Trace filter that skips two classnames
//-----------------------------------------------------------------------------
CTraceFilterSkipTwoClassnames : : CTraceFilterSkipTwoClassnames ( const IHandleEntity * passentity , const char * pchClassname , const char * pchClassname2 , int collisionGroup ) :
BaseClass ( passentity , pchClassname , collisionGroup ) , m_pchClassname2 ( pchClassname2 )
{
}
bool CTraceFilterSkipTwoClassnames : : ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
CBaseEntity * pEntity = EntityFromEntityHandle ( pHandleEntity ) ;
if ( ! pEntity | | FClassnameIs ( pEntity , m_pchClassname2 ) )
return false ;
return BaseClass : : ShouldHitEntity ( pHandleEntity , contentsMask ) ;
}
//-----------------------------------------------------------------------------
// Trace filter that can take a list of entities to ignore
//-----------------------------------------------------------------------------
CTraceFilterSimpleClassnameList : : CTraceFilterSimpleClassnameList ( const IHandleEntity * passentity , int collisionGroup ) :
CTraceFilterSimple ( passentity , collisionGroup )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTraceFilterSimpleClassnameList : : ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
CBaseEntity * pEntity = EntityFromEntityHandle ( pHandleEntity ) ;
if ( ! pEntity )
return false ;
for ( int i = 0 ; i < m_PassClassnames . Count ( ) ; + + i )
{
if ( FClassnameIs ( pEntity , m_PassClassnames [ i ] ) )
return false ;
}
return CTraceFilterSimple : : ShouldHitEntity ( pHandleEntity , contentsMask ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Add an entity to my list of entities to ignore in the trace
//-----------------------------------------------------------------------------
void CTraceFilterSimpleClassnameList : : AddClassnameToIgnore ( const char * pchClassname )
{
m_PassClassnames . AddToTail ( pchClassname ) ;
}
CTraceFilterChain : : CTraceFilterChain ( ITraceFilter * pTraceFilter1 , ITraceFilter * pTraceFilter2 )
{
m_pTraceFilter1 = pTraceFilter1 ;
m_pTraceFilter2 = pTraceFilter2 ;
}
bool CTraceFilterChain : : ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
bool bResult1 = true ;
bool bResult2 = true ;
if ( m_pTraceFilter1 )
bResult1 = m_pTraceFilter1 - > ShouldHitEntity ( pHandleEntity , contentsMask ) ;
if ( m_pTraceFilter2 )
bResult2 = m_pTraceFilter2 - > ShouldHitEntity ( pHandleEntity , contentsMask ) ;
return ( bResult1 & & bResult2 ) ;
}
//-----------------------------------------------------------------------------
// Sweeps against a particular model, using collision rules
//-----------------------------------------------------------------------------
void UTIL_TraceModel ( const Vector & vecStart , const Vector & vecEnd , const Vector & hullMin ,
const Vector & hullMax , CBaseEntity * pentModel , int collisionGroup , trace_t * ptr )
{
// Cull it....
if ( pentModel & & pentModel - > ShouldCollide ( collisionGroup , MASK_ALL ) )
{
Ray_t ray ;
ray . Init ( vecStart , vecEnd , hullMin , hullMax ) ;
enginetrace - > ClipRayToEntity ( ray , MASK_ALL , pentModel , ptr ) ;
}
else
{
memset ( ptr , 0 , sizeof ( trace_t ) ) ;
ptr - > fraction = 1.0f ;
}
}
bool UTIL_EntityHasMatchingRootParent ( CBaseEntity * pRootParent , CBaseEntity * pEntity )
{
if ( pRootParent )
{
// NOTE: Don't let siblings/parents collide.
if ( pRootParent = = pEntity - > GetRootMoveParent ( ) )
return true ;
if ( pEntity - > GetOwnerEntity ( ) & & pRootParent = = pEntity - > GetOwnerEntity ( ) - > GetRootMoveParent ( ) )
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Sweep an entity from the starting to the ending position
//-----------------------------------------------------------------------------
class CTraceFilterEntity : public CTraceFilterSimple
{
DECLARE_CLASS ( CTraceFilterEntity , CTraceFilterSimple ) ;
public :
CTraceFilterEntity ( CBaseEntity * pEntity , int nCollisionGroup )
: CTraceFilterSimple ( pEntity , nCollisionGroup )
{
m_pRootParent = pEntity - > GetRootMoveParent ( ) ;
m_pEntity = pEntity ;
m_checkHash = g_EntityCollisionHash - > IsObjectInHash ( pEntity ) ;
}
bool ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
CBaseEntity * pEntity = EntityFromEntityHandle ( pHandleEntity ) ;
if ( ! pEntity )
return false ;
// Check parents against each other
// NOTE: Don't let siblings/parents collide.
if ( UTIL_EntityHasMatchingRootParent ( m_pRootParent , pEntity ) )
return false ;
if ( m_checkHash )
{
if ( g_EntityCollisionHash - > IsObjectPairInHash ( m_pEntity , pEntity ) )
return false ;
}
# ifndef CLIENT_DLL
if ( m_pEntity - > IsNPC ( ) )
{
if ( NPC_CheckBrushExclude ( m_pEntity , pEntity ) )
return false ;
}
# endif
return BaseClass : : ShouldHitEntity ( pHandleEntity , contentsMask ) ;
}
private :
CBaseEntity * m_pRootParent ;
CBaseEntity * m_pEntity ;
bool m_checkHash ;
} ;
class CTraceFilterEntityIgnoreOther : public CTraceFilterEntity
{
DECLARE_CLASS ( CTraceFilterEntityIgnoreOther , CTraceFilterEntity ) ;
public :
CTraceFilterEntityIgnoreOther ( CBaseEntity * pEntity , const IHandleEntity * pIgnore , int nCollisionGroup ) :
CTraceFilterEntity ( pEntity , nCollisionGroup ) , m_pIgnoreOther ( pIgnore )
{
}
bool ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
if ( pHandleEntity = = m_pIgnoreOther )
return false ;
return BaseClass : : ShouldHitEntity ( pHandleEntity , contentsMask ) ;
}
private :
const IHandleEntity * m_pIgnoreOther ;
} ;
//-----------------------------------------------------------------------------
// Sweeps a particular entity through the world
//-----------------------------------------------------------------------------
void UTIL_TraceEntity ( CBaseEntity * pEntity , const Vector & vecAbsStart , const Vector & vecAbsEnd , unsigned int mask , trace_t * ptr )
{
ICollideable * pCollision = pEntity - > GetCollideable ( ) ;
// Adding this assertion here so game code catches it, but really the assertion belongs in the engine
// because one day, rotated collideables will work!
Assert ( pCollision - > GetCollisionAngles ( ) = = vec3_angle ) ;
CTraceFilterEntity traceFilter ( pEntity , pCollision - > GetCollisionGroup ( ) ) ;
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
enginetrace - > SweepCollideable ( pCollision , vecAbsStart , vecAbsEnd , pCollision - > GetCollisionAngles ( ) , mask , & traceFilter , ptr ) ;
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
}
void UTIL_TraceEntity ( CBaseEntity * pEntity , const Vector & vecAbsStart , const Vector & vecAbsEnd ,
unsigned int mask , const IHandleEntity * pIgnore , int nCollisionGroup , trace_t * ptr )
{
ICollideable * pCollision ;
pCollision = pEntity - > GetCollideable ( ) ;
// Adding this assertion here so game code catches it, but really the assertion belongs in the engine
// because one day, rotated collideables will work!
Assert ( pCollision - > GetCollisionAngles ( ) = = vec3_angle ) ;
CTraceFilterEntityIgnoreOther traceFilter ( pEntity , pIgnore , nCollisionGroup ) ;
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
enginetrace - > SweepCollideable ( pCollision , vecAbsStart , vecAbsEnd , pCollision - > GetCollisionAngles ( ) , mask , & traceFilter , ptr ) ;
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
}
void UTIL_TraceEntity ( CBaseEntity * pEntity , const Vector & vecAbsStart , const Vector & vecAbsEnd ,
unsigned int mask , ITraceFilter * pFilter , trace_t * ptr )
{
ICollideable * pCollision ;
pCollision = pEntity - > GetCollideable ( ) ;
// Adding this assertion here so game code catches it, but really the assertion belongs in the engine
// because one day, rotated collideables will work!
Assert ( pCollision - > GetCollisionAngles ( ) = = vec3_angle ) ;
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
enginetrace - > SweepCollideable ( pCollision , vecAbsStart , vecAbsEnd , pCollision - > GetCollisionAngles ( ) , mask , pFilter , ptr ) ;
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
}
// ----
// This is basically a regular TraceLine that uses the FilterEntity filter.
void UTIL_TraceLineFilterEntity ( CBaseEntity * pEntity , const Vector & vecAbsStart , const Vector & vecAbsEnd ,
unsigned int mask , int nCollisionGroup , trace_t * ptr )
{
CTraceFilterEntity traceFilter ( pEntity , nCollisionGroup ) ;
UTIL_TraceLine ( vecAbsStart , vecAbsEnd , mask , & traceFilter , ptr ) ;
}
void UTIL_ClipTraceToPlayers ( 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 ) ;
for ( int k = 1 ; k < = gpGlobals - > maxClients ; + + k )
{
CBasePlayer * player = UTIL_PlayerByIndex ( k ) ;
if ( ! player | | ! player - > IsAlive ( ) )
continue ;
# ifdef CLIENT_DLL
if ( player - > IsDormant ( ) )
continue ;
# endif // CLIENT_DLL
if ( filter & & filter - > ShouldHitEntity ( player , mask ) = = false )
continue ;
float range = DistanceToRay ( player - > WorldSpaceCenter ( ) , vecAbsStart , vecAbsEnd ) ;
if ( range < 0.0f | | range > maxRange )
continue ;
enginetrace - > ClipRayToEntity ( ray , mask | CONTENTS_HITBOX , player , & playerTrace ) ;
if ( playerTrace . fraction < smallestFraction )
{
// we shortened the ray - save off the trace
* tr = playerTrace ;
smallestFraction = playerTrace . fraction ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Make a tracer using a particle effect
//-----------------------------------------------------------------------------
void UTIL_ParticleTracer ( const char * pszTracerEffectName , const Vector & vecStart , const Vector & vecEnd ,
int iEntIndex , int iAttachment , bool bWhiz )
{
int iParticleIndex = GetParticleSystemIndex ( pszTracerEffectName ) ;
UTIL_Tracer ( vecStart , vecEnd , iEntIndex , iAttachment , 0 , bWhiz , " ParticleTracer " , iParticleIndex ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Make a tracer effect using the old, non-particle system, tracer effects.
//-----------------------------------------------------------------------------
void UTIL_Tracer ( const Vector & vecStart , const Vector & vecEnd , int iEntIndex ,
int iAttachment , float flVelocity , bool bWhiz , const char * pCustomTracerName , int iParticleID )
{
CEffectData data ;
data . m_vStart = vecStart ;
data . m_vOrigin = vecEnd ;
# ifdef CLIENT_DLL
data . m_hEntity = ClientEntityList ( ) . EntIndexToHandle ( iEntIndex ) ;
# else
data . m_nEntIndex = iEntIndex ;
# endif
data . m_flScale = flVelocity ;
data . m_nHitBox = iParticleID ;
// Flags
if ( bWhiz )
{
data . m_fFlags | = TRACER_FLAG_WHIZ ;
}
if ( iAttachment ! = TRACER_DONT_USE_ATTACHMENT )
{
data . m_fFlags | = TRACER_FLAG_USEATTACHMENT ;
2023-10-03 14:23:56 +00:00
// Stomp the start, since it's not going to be used anyway
2020-04-22 16:56:21 +00:00
data . m_nAttachmentIndex = iAttachment ;
}
// Fire it off
if ( pCustomTracerName )
{
DispatchEffect ( pCustomTracerName , data ) ;
}
else
{
DispatchEffect ( " Tracer " , data ) ;
}
}
void UTIL_BloodDrips ( const Vector & origin , const Vector & direction , int color , int amount )
{
if ( ! UTIL_ShouldShowBlood ( color ) )
return ;
if ( color = = DONT_BLEED | | amount = = 0 )
return ;
if ( g_Language . GetInt ( ) = = LANGUAGE_GERMAN & & color = = BLOOD_COLOR_RED )
color = 0 ;
if ( g_pGameRules - > IsMultiplayer ( ) )
{
// scale up blood effect in multiplayer for better visibility
amount * = 5 ;
}
if ( amount > 255 )
amount = 255 ;
if ( color = = BLOOD_COLOR_MECH )
{
g_pEffects - > Sparks ( origin ) ;
if ( random - > RandomFloat ( 0 , 2 ) > = 1 )
{
UTIL_Smoke ( origin , random - > RandomInt ( 10 , 15 ) , 10 ) ;
}
}
else
{
// Normal blood impact
UTIL_BloodImpact ( origin , direction , color , amount ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns low violence settings
//-----------------------------------------------------------------------------
static ConVar violence_hblood ( " violence_hblood " , " 1 " , 0 , " Draw human blood " ) ;
static ConVar violence_hgibs ( " violence_hgibs " , " 1 " , 0 , " Show human gib entities " ) ;
static ConVar violence_ablood ( " violence_ablood " , " 1 " , 0 , " Draw alien blood " ) ;
static ConVar violence_agibs ( " violence_agibs " , " 1 " , 0 , " Show alien gib entities " ) ;
bool UTIL_IsLowViolence ( void )
{
// These convars are no longer necessary -- the engine is the final arbiter of
// violence settings -- but they're here for legacy support and for testing low
// violence when the engine is in normal violence mode.
if ( ! violence_hblood . GetBool ( ) | | ! violence_ablood . GetBool ( ) | | ! violence_hgibs . GetBool ( ) | | ! violence_agibs . GetBool ( ) )
return true ;
return engine - > IsLowViolence ( ) ;
}
bool UTIL_ShouldShowBlood ( int color )
{
if ( color ! = DONT_BLEED )
{
if ( color = = BLOOD_COLOR_RED )
{
return violence_hblood . GetBool ( ) ;
}
else
{
return violence_ablood . GetBool ( ) ;
}
}
return false ;
}
//------------------------------------------------------------------------------
// Purpose : Use trace to pass a specific decal type to the entity being decaled
// Input :
// Output :
//------------------------------------------------------------------------------
void UTIL_DecalTrace ( trace_t * pTrace , char const * decalName )
{
if ( pTrace - > fraction = = 1.0 )
return ;
CBaseEntity * pEntity = pTrace - > m_pEnt ;
2023-10-03 14:23:56 +00:00
if ( ! pEntity )
return ;
2020-04-22 16:56:21 +00:00
pEntity - > DecalTrace ( pTrace , decalName ) ;
}
void UTIL_BloodDecalTrace ( trace_t * pTrace , int bloodColor )
{
if ( UTIL_ShouldShowBlood ( bloodColor ) )
{
if ( bloodColor = = BLOOD_COLOR_RED )
{
UTIL_DecalTrace ( pTrace , " Blood " ) ;
}
2023-10-03 14:23:56 +00:00
# if defined( HL2_EPISODIC )
else if ( bloodColor = = BLOOD_COLOR_BLOB )
{
UTIL_DecalTrace ( pTrace , " BlobBlood " ) ;
}
//don't draw a any decals if the blob is frozen
else if ( bloodColor = = BLOOD_COLOR_BLOB_FROZEN )
{
return ;
}
# endif
else if ( bloodColor = = BLOOD_COLOR_BRIGHTGREEN )
{
UTIL_DecalTrace ( pTrace , " GreenBlood " ) ;
}
2020-04-22 16:56:21 +00:00
else
{
UTIL_DecalTrace ( pTrace , " YellowBlood " ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &pos -
// &dir -
// color -
// amount -
//-----------------------------------------------------------------------------
void UTIL_BloodImpact ( const Vector & pos , const Vector & dir , int color , int amount )
{
CEffectData data ;
data . m_vOrigin = pos ;
data . m_vNormal = dir ;
data . m_flScale = ( float ) amount ;
data . m_nColor = ( unsigned char ) color ;
DispatchEffect ( " bloodimpact " , data ) ;
}
bool UTIL_IsSpaceEmpty ( CBaseEntity * pMainEnt , const Vector & vMin , const Vector & vMax )
{
Vector vHalfDims = ( vMax - vMin ) * 0.5f ;
Vector vCenter = vMin + vHalfDims ;
trace_t trace ;
2023-10-03 14:23:56 +00:00
int mask = ( pMainEnt ) ? pMainEnt - > PhysicsSolidMaskForEntity ( ) : MASK_SOLID ;
UTIL_TraceHull ( vCenter , vCenter , - vHalfDims , vHalfDims , mask , pMainEnt , COLLISION_GROUP_NONE , & trace ) ;
bool bClear = ( trace . fraction = = 1 & & trace . allsolid ! = 1 & & ( trace . startsolid ! = 1 ) ) ;
return bClear ;
}
bool UTIL_IsSpaceEmpty ( CBaseEntity * pMainEnt , const Vector & vMin , const Vector & vMax , unsigned int mask , ITraceFilter * pFilter )
{
Vector vHalfDims = ( vMax - vMin ) * 0.5f ;
Vector vCenter = vMin + vHalfDims ;
trace_t trace ;
UTIL_TraceHull ( vCenter , vCenter , - vHalfDims , vHalfDims , mask , pFilter , & trace ) ;
2020-04-22 16:56:21 +00:00
bool bClear = ( trace . fraction = = 1 & & trace . allsolid ! = 1 & & ( trace . startsolid ! = 1 ) ) ;
return bClear ;
}
void UTIL_StringToFloatArray ( float * pVector , int count , const char * pString )
{
char * pstr , * pfront , tempString [ 128 ] ;
int j ;
Q_strncpy ( tempString , pString , sizeof ( tempString ) ) ;
pstr = pfront = tempString ;
for ( j = 0 ; j < count ; j + + ) // lifted from pr_edict.c
{
pVector [ j ] = atof ( pfront ) ;
// skip any leading whitespace
while ( * pstr & & * pstr < = ' ' )
pstr + + ;
// skip to next whitespace
while ( * pstr & & * pstr > ' ' )
pstr + + ;
if ( ! * pstr )
break ;
pstr + + ;
pfront = pstr ;
}
for ( j + + ; j < count ; j + + )
{
pVector [ j ] = 0 ;
}
}
void UTIL_StringToVector ( float * pVector , const char * pString )
{
UTIL_StringToFloatArray ( pVector , 3 , pString ) ;
}
# ifndef _XBOX
2023-10-03 14:23:56 +00:00
void UTIL_DecodeICE ( unsigned char * buffer , int size , const unsigned char * key )
2020-04-22 16:56:21 +00:00
{
if ( ! key )
return ;
IceKey ice ( 0 ) ; // level 0 = 64bit key
ice . set ( key ) ; // set key
int blockSize = ice . blockSize ( ) ;
2023-10-03 14:23:56 +00:00
unsigned char * temp = ( unsigned char * ) stackalloc ( PAD_NUMBER ( size , blockSize ) ) ;
2020-04-22 16:56:21 +00:00
unsigned char * p1 = buffer ;
unsigned char * p2 = temp ;
// encrypt data in 8 byte blocks
int bytesLeft = size ;
while ( bytesLeft > = blockSize )
{
ice . decrypt ( p1 , p2 ) ;
bytesLeft - = blockSize ;
p1 + = blockSize ;
p2 + = blockSize ;
}
// copy encrypted data back to original buffer
Q_memcpy ( buffer , temp , size - bytesLeft ) ;
}
2023-10-03 14:23:56 +00:00
void UTIL_EncodeICE ( unsigned char * buffer , unsigned int size , const unsigned char * key )
{
if ( ! key )
return ;
IceKey ice ( 0 ) ; // level 0 = 64bit key
ice . set ( key ) ; // set key
unsigned char * cipherText = buffer ;
unsigned char * plainText = buffer ;
uint bytesEncrypted = 0 ;
while ( bytesEncrypted < size )
{
ice . encrypt ( plainText , cipherText ) ;
bytesEncrypted + = 8 ;
cipherText + = 8 ;
plainText + = 8 ;
}
}
2020-04-22 16:56:21 +00:00
# endif
// work-around since client header doesn't like inlined gpGlobals->curtime
float IntervalTimer : : Now ( void ) const
{
return gpGlobals - > curtime ;
}
// work-around since client header doesn't like inlined gpGlobals->curtime
float CountdownTimer : : Now ( void ) const
{
return gpGlobals - > curtime ;
}
2023-10-03 14:23:56 +00:00
BEGIN_DATADESC_NO_BASE ( IntervalTimer )
END_DATADESC ( )
BEGIN_NETWORK_TABLE_NOBASE ( IntervalTimer , DT_IntervalTimer )
2020-04-22 16:56:21 +00:00
# ifdef CLIENT_DLL
2023-10-03 14:23:56 +00:00
RecvPropFloat ( RECVINFO ( m_timestamp ) ) ,
# else
SendPropFloat ( SENDINFO ( m_timestamp ) , 0 , SPROP_NOSCALE ) ,
# endif
END_NETWORK_TABLE ( )
# ifdef CLIENT_DLL
BEGIN_PREDICTION_DATA_NO_BASE ( IntervalTimer )
DEFINE_PRED_FIELD ( m_timestamp , FIELD_FLOAT , FTYPEDESC_INSENDTABLE ) ,
END_PREDICTION_DATA ( )
# endif
# ifdef CLIENT_DLL
BEGIN_RECV_TABLE_NOBASE ( CountdownTimer , DT_CountdownTimer )
RecvPropFloat ( RECVINFO ( m_duration ) ) ,
RecvPropFloat ( RECVINFO ( m_timestamp ) ) ,
END_RECV_TABLE ( )
BEGIN_PREDICTION_DATA_NO_BASE ( CountdownTimer )
DEFINE_PRED_FIELD ( m_duration , FIELD_FLOAT , FTYPEDESC_INSENDTABLE ) ,
DEFINE_PRED_FIELD ( m_timestamp , FIELD_FLOAT , FTYPEDESC_INSENDTABLE ) ,
END_PREDICTION_DATA ( )
# else
BEGIN_SEND_TABLE_NOBASE ( CountdownTimer , DT_CountdownTimer )
SendPropFloat ( SENDINFO ( m_duration ) , 0 , SPROP_NOSCALE ) ,
SendPropFloat ( SENDINFO ( m_timestamp ) , 0 , SPROP_NOSCALE ) ,
END_SEND_TABLE ( )
# endif
BEGIN_DATADESC ( CTimeline )
DEFINE_ARRAY ( m_flValues , FIELD_FLOAT , TIMELINE_ARRAY_SIZE ) ,
DEFINE_ARRAY ( m_nValueCounts , FIELD_FLOAT , TIMELINE_ARRAY_SIZE ) ,
DEFINE_FIELD ( m_nBucketCount , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_flInterval , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_flFinalValue , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_nCompressionType , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_bStopped , FIELD_BOOLEAN ) ,
END_DATADESC ( )
BEGIN_NETWORK_TABLE_NOBASE ( CTimeline , DT_Timeline )
# ifdef CLIENT_DLL
RecvPropArray3 ( RECVINFO_ARRAY ( m_flValues ) , RecvPropFloat ( RECVINFO ( m_flValues [ 0 ] ) ) ) ,
RecvPropArray3 ( RECVINFO_ARRAY ( m_nValueCounts ) , RecvPropFloat ( RECVINFO ( m_nValueCounts [ 0 ] ) ) ) ,
RecvPropInt ( RECVINFO ( m_nBucketCount ) ) ,
RecvPropFloat ( RECVINFO ( m_flInterval ) ) ,
RecvPropFloat ( RECVINFO ( m_flFinalValue ) ) ,
RecvPropInt ( RECVINFO ( m_nCompressionType ) ) ,
RecvPropBool ( RECVINFO ( m_bStopped ) ) ,
# else
SendPropArray3 ( SENDINFO_ARRAY3 ( m_flValues ) , SendPropFloat ( SENDINFO_ARRAY ( m_flValues ) , 0 , SPROP_NOSCALE ) ) ,
SendPropArray3 ( SENDINFO_ARRAY3 ( m_nValueCounts ) , SendPropFloat ( SENDINFO_ARRAY ( m_nValueCounts ) , 0 , SPROP_NOSCALE ) ) ,
SendPropInt ( SENDINFO ( m_nBucketCount ) , NumBitsForCount ( TIMELINE_ARRAY_SIZE ) , SPROP_UNSIGNED ) ,
SendPropFloat ( SENDINFO ( m_flInterval ) , 0 , SPROP_NOSCALE ) ,
SendPropFloat ( SENDINFO ( m_flFinalValue ) , 0 , SPROP_NOSCALE ) ,
SendPropInt ( SENDINFO ( m_nCompressionType ) , - 1 , SPROP_UNSIGNED ) ,
SendPropBool ( SENDINFO ( m_bStopped ) ) ,
# endif
END_NETWORK_TABLE ( )
void CTimeline : : ClearValues ( void )
{
Invalidate ( ) ;
memset ( m_flValues . m_Value , 0 , sizeof ( m_flValues . m_Value ) ) ;
memset ( m_nValueCounts . m_Value , 0 , sizeof ( m_nValueCounts . m_Value ) ) ;
m_nBucketCount = 0 ;
m_flInterval = TIMELINE_INTERVAL_START ;
m_flFinalValue = 0.0f ;
m_bStopped = false ;
}
void CTimeline : : RecordValue ( float flValue )
{
if ( ! HasStarted ( ) | | m_bStopped )
return ;
int iBucket = GetCurrentBucket ( ) ;
Assert ( iBucket > = 0 ) ;
while ( iBucket > = TIMELINE_ARRAY_SIZE )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
Compress ( ) ;
iBucket = GetCurrentBucket ( ) ;
}
if ( iBucket > = m_nBucketCount )
{
m_nBucketCount = iBucket + 1 ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
m_flValues . GetForModify ( iBucket ) + = flValue ;
m_nValueCounts . GetForModify ( iBucket ) + + ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
if ( m_nCompressionType = = TIMELINE_COMPRESSION_SUM | |
m_nCompressionType = = TIMELINE_COMPRESSION_AVERAGE | |
m_nCompressionType = = TIMELINE_COMPRESSION_AVERAGE_BLEND )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
// Fill in blank preceding entries
float flCurrentValue = m_flValues [ iBucket ] / m_nValueCounts [ iBucket ] ;
int iPrecedingBucket = iBucket - 1 ;
while ( iPrecedingBucket > = 0 & & m_nValueCounts [ iPrecedingBucket ] < = 0 )
{
iPrecedingBucket - - ;
}
// Get the last logged value (or the current if this is the first)
float flPrecedingValue = flCurrentValue ;
if ( iPrecedingBucket > = 0 )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
// Last logged value
if ( m_nCompressionType = = TIMELINE_COMPRESSION_SUM )
{
flPrecedingValue = m_flValues [ iPrecedingBucket ] ;
if ( m_nValueCounts [ iBucket ] = = 1 )
{
// Sum in the previous bucket if this is the first value in this bucket
m_flValues . GetForModify ( iBucket ) + = flPrecedingValue ;
}
}
else
{
flPrecedingValue = m_flFinalValue ;
}
}
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
// Number of buckets for blending from old to new value
float flNumBuckets = ( iBucket - iPrecedingBucket ) + 1 ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
if ( flNumBuckets > = 3.0f )
{
if ( m_nCompressionType = = TIMELINE_COMPRESSION_AVERAGE_BLEND )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
// Blend empty values between preceding and current
float flInterpBucket = 1.0f ;
for ( int i = iPrecedingBucket + 1 ; i < iBucket ; + + i , flInterpBucket + = 1.0f )
{
float flInterp = flInterpBucket / flNumBuckets ;
m_flValues . Set ( i , flPrecedingValue * ( 1.0f - flInterp ) + flCurrentValue * flInterp ) ;
m_nValueCounts . Set ( i , 1 ) ;
}
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
else
{
// Set empty values to preceding value
for ( int i = iPrecedingBucket + 1 ; i < iBucket ; + + i )
{
m_flValues . Set ( i , flPrecedingValue ) ;
m_nValueCounts . Set ( i , 1 ) ;
}
}
}
}
m_flFinalValue = flValue ;
}
float CTimeline : : GetValue ( int i ) const
{
Assert ( i > = 0 & & i < m_nBucketCount ) ;
if ( i < 0 | | i > = m_nBucketCount )
{
return 0.0f ;
}
if ( m_nValueCounts [ i ] < = 0 )
{
return 0.0f ;
}
else if ( i = = 0 & & m_nCompressionType = = TIMELINE_COMPRESSION_SUM & & m_nBucketCount > 1 )
{
// Aways start at 0 for sums!
return 0.0 ;
}
else
{
switch ( m_nCompressionType )
{
case TIMELINE_COMPRESSION_AVERAGE :
case TIMELINE_COMPRESSION_AVERAGE_BLEND :
return m_flValues [ i ] / m_nValueCounts [ i ] ;
case TIMELINE_COMPRESSION_SUM :
case TIMELINE_COMPRESSION_COUNT_PER_INTERVAL :
default :
return m_flValues [ i ] ;
}
}
}
float CTimeline : : GetValueAtInterp ( float fInterp ) const
{
if ( fInterp < = 0.0f )
{
return GetValue ( 0 ) ;
}
if ( fInterp > = 1.0f )
{
if ( m_nCompressionType = = TIMELINE_COMPRESSION_SUM | |
m_nCompressionType = = TIMELINE_COMPRESSION_COUNT_PER_INTERVAL )
{
return GetValue ( Count ( ) - 1 ) ;
}
else
{
return m_flFinalValue ;
}
}
float fBucket = fInterp * ( Count ( ) - 1 ) ;
int nBucket = fBucket ;
fBucket - = nBucket ;
float fValue = GetValue ( nBucket ) ;
float fNextValue = GetValue ( nBucket + 1 ) ;
return fValue * ( 1.0f - fBucket ) + fNextValue * fBucket ;
}
void CTimeline : : Compress ( void )
{
int i , j ;
switch ( m_nCompressionType )
{
case TIMELINE_COMPRESSION_SUM :
for ( i = 0 , j = 0 ; i < TIMELINE_ARRAY_SIZE ; i + = 2 , + + j )
{
m_flValues . GetForModify ( j ) = MAX ( m_flValues [ i ] , m_flValues [ i + 1 ] ) ;
m_nValueCounts . GetForModify ( j ) = m_nValueCounts [ i ] + m_nValueCounts [ i + 1 ] ;
}
break ;
default :
for ( i = 0 , j = 0 ; i < TIMELINE_ARRAY_SIZE ; i + = 2 , + + j )
{
m_flValues . GetForModify ( j ) = m_flValues [ i ] + m_flValues [ i + 1 ] ;
m_nValueCounts . GetForModify ( j ) = m_nValueCounts [ i ] + m_nValueCounts [ i + 1 ] ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
break ;
}
int nRemainingBytes = ( TIMELINE_ARRAY_SIZE - j ) * sizeof ( m_flValues [ 0 ] ) ;
memset ( & ( m_flValues . GetForModify ( j ) ) , 0 , nRemainingBytes ) ;
memset ( & ( m_nValueCounts . GetForModify ( j ) ) , 0 , nRemainingBytes ) ;
m_flInterval * = 2.0f ;
m_nBucketCount = j ;
}
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
# ifdef CLIENT_DLL
CBasePlayer * UTIL_PlayerByIndex ( int entindex )
{
// Sanity check the index being passed in
if ( entindex < 1 | | entindex > gpGlobals - > maxClients )
return NULL ;
return ToBasePlayer ( ClientEntityList ( ) . GetEnt ( entindex ) ) ;
2022-03-01 20:00:42 +00:00
}
2023-10-03 14:23:56 +00:00
# endif
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
unsigned short UTIL_GetAchievementEventMask ( void )
{
CRC32_t mapCRC ;
CRC32_Init ( & mapCRC ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
char lowercase [ 256 ] ;
# ifdef CLIENT_DLL
Q_FileBase ( engine - > GetLevelName ( ) , lowercase , sizeof ( lowercase ) ) ;
# else
Q_strncpy ( lowercase , STRING ( gpGlobals - > mapname ) , sizeof ( lowercase ) ) ;
2020-04-22 16:56:21 +00:00
# endif
2023-10-03 14:23:56 +00:00
Q_strlower ( lowercase ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
CRC32_ProcessBuffer ( & mapCRC , lowercase , Q_strlen ( lowercase ) ) ;
CRC32_Final ( & mapCRC ) ;
return ( mapCRC & 0xFFFF ) ;
}
2020-04-22 16:56:21 +00:00
char * ReadAndAllocStringValue ( KeyValues * pSub , const char * pName , const char * pFilename )
{
const char * pValue = pSub - > GetString ( pName , NULL ) ;
if ( ! pValue )
{
if ( pFilename )
{
DevWarning ( " Can't get key value '%s' from file '%s'. \n " , pName , pFilename ) ;
}
return " " ;
}
int len = Q_strlen ( pValue ) + 1 ;
char * pAlloced = new char [ len ] ;
Assert ( pAlloced ) ;
Q_strncpy ( pAlloced , pValue , len ) ;
return pAlloced ;
}
int UTIL_StringFieldToInt ( const char * szValue , const char * * pValueStrings , int iNumStrings )
{
if ( ! szValue | | ! szValue [ 0 ] )
return - 1 ;
for ( int i = 0 ; i < iNumStrings ; i + + )
{
if ( FStrEq ( szValue , pValueStrings [ i ] ) )
return i ;
}
Assert ( 0 ) ;
return - 1 ;
}
2023-10-03 14:23:56 +00:00
static char s_NumBitsInNibble [ 16 ] =
{
0 , // 0000 = 0
1 , // 0001 = 1
1 , // 0010 = 2
2 , // 0011 = 3
1 , // 0100 = 4
2 , // 0101 = 5
2 , // 0110 = 6
3 , // 0111 = 7
1 , // 1000 = 8
2 , // 1001 = 9
2 , // 1010 = 10
3 , // 1011 = 11
2 , // 1100 = 12
3 , // 1101 = 13
3 , // 1110 = 14
4 , // 1111 = 15
} ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
int UTIL_CountNumBitsSet ( unsigned int nVar )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
int nNumBits = 0 ;
while ( nVar > 0 )
{
// Look up and add in bits in the bottom nibble
nNumBits + = s_NumBitsInNibble [ nVar & 0x0f ] ;
// Shift one nibble to the right
nVar > > = 4 ;
}
return nNumBits ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
int UTIL_CountNumBitsSet ( uint64 nVar )
{
int nNumBits = 0 ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
while ( nVar > 0 )
{
// Look up and add in bits in the bottom nibble
nNumBits + = s_NumBitsInNibble [ nVar & 0x0f ] ;
// Shift one nibble to the right
nVar > > = 4 ;
}
return nNumBits ;
}
bool UTIL_FindClosestPassableSpace ( const Vector & vOriginalCenter , const Vector & vExtents , const Vector & vIndecisivePush , ITraceFilter * pTraceFilter , unsigned int fMask , unsigned int iIterations , Vector & vCenterOut , int nAxisRestrictionFlags )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
Assert ( vExtents ! = vec3_origin ) ;
trace_t traces [ 2 ] ;
Ray_t entRay ;
entRay . m_Extents = vExtents ;
entRay . m_IsRay = false ;
entRay . m_IsSwept = true ;
entRay . m_StartOffset = vec3_origin ;
Vector vOriginalExtents = vExtents ;
Vector vCenter = vOriginalCenter ;
Vector vGrowSize = vExtents * ( 1.0f / ( float ) ( iIterations + 1 ) ) ;
Vector vCurrentExtents = vExtents - vGrowSize ;
int iLargestExtent = 0 ;
{
float fLargestExtent = vOriginalExtents [ 0 ] ;
for ( int i = 1 ; i ! = 3 ; + + i )
{
if ( vOriginalExtents [ i ] > fLargestExtent )
{
iLargestExtent = i ;
fLargestExtent = vOriginalExtents [ i ] ;
}
}
}
Ray_t testRay ;
testRay . m_Extents = vGrowSize ;
testRay . m_IsRay = false ;
testRay . m_IsSwept = true ;
testRay . m_StartOffset = vec3_origin ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
float fOriginalExtentDists [ 8 ] ; //distance between extents
//generate distance lookup. We reference this by XOR'ing the indices of two extents to find the axis of difference
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
//Since the ratios of lengths never change, we're going to normalize these distances to a value so we can simply scale on each iteration
//We've picked the largest extent as the basis simply because it's nonzero
float fNormalizer = 1.0f / vOriginalExtents [ iLargestExtent ] ;
float fXDiff = vOriginalExtents . x * 2.0f * fNormalizer ;
float fXSqr = fXDiff * fXDiff ;
float fYDiff = vOriginalExtents . y * 2.0f * fNormalizer ;
float fYSqr = fYDiff * fYDiff ;
float fZDiff = vOriginalExtents . z * 2.0f * fNormalizer ;
float fZSqr = fZDiff * fZDiff ;
fOriginalExtentDists [ 0 ] = 0.0f ; //should never get hit
fOriginalExtentDists [ 1 ] = fXDiff ; //line along x axis
fOriginalExtentDists [ 2 ] = fYDiff ; //line along y axis
fOriginalExtentDists [ 3 ] = sqrt ( fXSqr + fYSqr ) ; //diagonal perpendicular to z-axis
fOriginalExtentDists [ 4 ] = fZDiff ; //line along z axis
fOriginalExtentDists [ 5 ] = sqrt ( fXSqr + fZSqr ) ; //diagonal perpendicular to y-axis
fOriginalExtentDists [ 6 ] = sqrt ( fYSqr + fZSqr ) ; //diagonal perpendicular to x-axis
fOriginalExtentDists [ 7 ] = sqrt ( fXSqr + fYSqr + fZSqr ) ; //diagonal on all axes
}
Vector ptExtents [ 8 ] ; //ordering is going to be like 3 bits, where 0 is a min on the related axis, and 1 is a max on the same axis, axis order x y z
float fExtentsValidation [ 8 ] ; //some points are more valid than others, and this is our measure
vCenter . z + = 0.001f ; //to satisfy m_IsSwept on first pass
unsigned int iFailCount ;
for ( iFailCount = 0 ; iFailCount ! = iIterations ; + + iFailCount )
{
//float fXDistribution[2] = { -vCurrentExtents.x, vCurrentExtents.x };
//float fYDistribution[3] = { -vCurrentExtents.y, 0.0f, vCurrentExtents.y };
//float fZDistribution[5] = { -vCurrentExtents.z, 0.0f, 0.0f, 0.0f, vCurrentExtents.z };
//hey look, they can overlap
float fExtentDistribution [ 6 ] ;
fExtentDistribution [ 0 ] = vCenter . z + ( ( ( nAxisRestrictionFlags & FL_AXIS_DIRECTION_NZ ) = = 0 ) ? ( - vCurrentExtents . z ) : ( 0.0f ) ) ; // Z-
fExtentDistribution [ 1 ] = vCenter . x + ( ( ( nAxisRestrictionFlags & FL_AXIS_DIRECTION_NX ) = = 0 ) ? ( - vCurrentExtents . x ) : ( 0.0f ) ) ; // X-
fExtentDistribution [ 2 ] = vCenter . x + ( ( ( nAxisRestrictionFlags & FL_AXIS_DIRECTION_X ) = = 0 ) ? ( vCurrentExtents . x ) : ( 0.0f ) ) ; // X+
fExtentDistribution [ 3 ] = vCenter . y + ( ( ( nAxisRestrictionFlags & FL_AXIS_DIRECTION_NY ) = = 0 ) ? ( - vCurrentExtents . y ) : ( 0.0f ) ) ; // Y-
fExtentDistribution [ 4 ] = vCenter . z + ( ( ( nAxisRestrictionFlags & FL_AXIS_DIRECTION_Z ) = = 0 ) ? ( vCurrentExtents . z ) : ( 0.0f ) ) ; // Z+
fExtentDistribution [ 5 ] = vCenter . y + ( ( ( nAxisRestrictionFlags & FL_AXIS_DIRECTION_Y ) = = 0 ) ? ( vCurrentExtents . y ) : ( 0.0f ) ) ; // Y+
float * pXDistribution = & fExtentDistribution [ 1 ] ;
float * pYDistribution = & fExtentDistribution [ 3 ] ;
bool bExtentInvalid [ 8 ] ;
float fExtentDists [ 8 ] ;
bool bAnyInvalid = false ;
for ( int i = 0 ; i ! = 8 ; + + i )
{
ptExtents [ i ] . x = pXDistribution [ i & ( 1 < < 0 ) ] ; //fExtentDistribution[(0 or 1) + 1]
ptExtents [ i ] . y = pYDistribution [ i & ( 1 < < 1 ) ] ; //fExtentDistribution[(0 or 2) + 3]
ptExtents [ i ] . z = fExtentDistribution [ i & ( 1 < < 2 ) ] ; //fExtentDistribution[(0 or 4)]
fExtentsValidation [ i ] = 0.0f ;
bExtentInvalid [ i ] = enginetrace - > PointOutsideWorld ( ptExtents [ i ] ) ;
bAnyInvalid | = bExtentInvalid [ i ] ;
fExtentDists [ i ] = fOriginalExtentDists [ i ] * vExtents [ iLargestExtent ] ;
}
//trace from all extents to all other extents and rate the validity
{
unsigned int counters [ 2 ] ; //I know it's weird, get over it
for ( counters [ 0 ] = 0 ; counters [ 0 ] ! = 7 ; + + counters [ 0 ] )
{
for ( counters [ 1 ] = counters [ 0 ] + 1 ; counters [ 1 ] ! = 8 ; + + counters [ 1 ] )
{
for ( int i = 0 ; i ! = 2 ; + + i )
{
if ( bExtentInvalid [ counters [ i ] ] )
{
traces [ i ] . startsolid = true ;
traces [ i ] . fraction = 0.0f ;
}
else
{
testRay . m_Start = ptExtents [ counters [ i ] ] ;
testRay . m_Delta = ptExtents [ counters [ 1 - i ] ] - ptExtents [ counters [ i ] ] ;
enginetrace - > TraceRay ( testRay , fMask , pTraceFilter , & traces [ i ] ) ;
}
}
float fDistance = fExtentDists [ counters [ 0 ] ^ counters [ 1 ] ] ;
for ( int i = 0 ; i ! = 2 ; + + i )
{
if ( ( traces [ i ] . fraction = = 1.0f ) & & ( traces [ 1 - i ] . fraction ! = 1.0f ) )
{
//One sided collision >_<
traces [ i ] . startsolid = true ;
traces [ i ] . fraction = 0.0f ;
break ;
}
}
for ( int i = 0 ; i ! = 2 ; + + i )
{
if ( traces [ i ] . startsolid )
{
bExtentInvalid [ counters [ i ] ] = true ;
bAnyInvalid = true ;
}
else
{
fExtentsValidation [ counters [ i ] ] + = traces [ i ] . fraction * fDistance ;
}
}
}
}
}
//optimally we should do this check before tracing extents. But one sided collision is a bitch
if ( ! bAnyInvalid )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
//try to trace back to the starting position (if we start in valid, the endpoint will be closer to the original center)
entRay . m_Start = vCenter ;
entRay . m_Delta = vOriginalCenter - vCenter ;
enginetrace - > TraceRay ( entRay , fMask , pTraceFilter , & traces [ 0 ] ) ;
if ( traces [ 0 ] . startsolid = = false )
{
//damned one sided collision
vCenterOut = traces [ 0 ] . endpos ;
return true ; //current placement worked
}
}
//find the direction to move based on the extent validity
{
Vector vNewOriginDirection ( 0.0f , 0.0f , 0.0f ) ;
float fTotalValidation = 0.0f ;
for ( int i = 0 ; i ! = 8 ; + + i )
{
if ( ! bExtentInvalid [ i ] )
{
vNewOriginDirection + = ( ptExtents [ i ] - vCenter ) * fExtentsValidation [ i ] ;
fTotalValidation + = fExtentsValidation [ i ] ;
}
}
if ( fTotalValidation ! = 0.0f )
{
vCenter + = ( vNewOriginDirection / fTotalValidation ) ;
//increase sizing
testRay . m_Extents + = vGrowSize ; //increase the ray size
vCurrentExtents - = vGrowSize ; //while reducing the overall test region size (so outermost ray extents are the same)
}
else
{
//no point was valid, apply the indecisive vector
vCenter + = vIndecisivePush ;
//reset sizing
testRay . m_Extents = vGrowSize ;
vCurrentExtents = vOriginalExtents - vGrowSize ;
}
2020-04-22 16:56:21 +00:00
}
}
2023-10-03 14:23:56 +00:00
//Warning( "FindClosestPassableSpace() failure.\n" );
// X360TBD: Hits in portal devtest
//AssertMsg( IsX360() || iFailCount != iIterations, "FindClosestPassableSpace() failure." );
vCenterOut = vOriginalCenter ;
return false ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
bool UTIL_FindClosestPassableSpace ( CBaseEntity * pEntity , const Vector & vIndecisivePush , unsigned int fMask , unsigned int iIterations , Vector & vOriginOut , Vector * pStartingPosition , int nAxisRestrictionFlags ) //assumes the object is already in a mostly passable space
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
// Don't ever do this to entities with a move parent
if ( pEntity - > GetMoveParent ( ) )
{
vOriginOut = pEntity - > GetAbsOrigin ( ) ;
2020-04-22 16:56:21 +00:00
return false ;
2023-10-03 14:23:56 +00:00
}
Vector vEntityMaxs ;
Vector vEntityMins ;
pEntity - > CollisionProp ( ) - > WorldSpaceAABB ( & vEntityMins , & vEntityMaxs ) ;
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
Vector ptEntityCenter = ( ( vEntityMins + vEntityMaxs ) / 2.0f ) ;
//vEntityMins -= ptEntityCenter;
vEntityMaxs - = ptEntityCenter ;
Vector vCenterToOrigin = pEntity - > GetAbsOrigin ( ) - ptEntityCenter ;
if ( pStartingPosition ! = NULL )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
Vector vOriginOffset = ( * pStartingPosition ) - pEntity - > GetAbsOrigin ( ) ;
ptEntityCenter + = vOriginOffset ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
CTraceFilterSimple traceFilter ( pEntity , pEntity - > GetCollisionGroup ( ) ) ;
Vector vResult ;
bool bSuccess = UTIL_FindClosestPassableSpace ( ptEntityCenter , vEntityMaxs , vIndecisivePush , & traceFilter , fMask , iIterations , vResult , nAxisRestrictionFlags ) ;
vOriginOut = vResult + vCenterToOrigin ;
return bSuccess ;
}
bool UTIL_FindClosestPassableSpace ( CBaseEntity * pEntity , const Vector & vIndecisivePush , unsigned int fMask , Vector * pStartingPosition , int nAxisRestrictionFlags )
{
Vector vNewPos ;
bool bWorked = UTIL_FindClosestPassableSpace ( pEntity , vIndecisivePush , fMask , 100 , vNewPos , pStartingPosition , nAxisRestrictionFlags ) ;
if ( bWorked )
{
# ifdef CLIENT_DLL
pEntity - > SetAbsOrigin ( vNewPos ) ;
2020-04-22 16:56:21 +00:00
# else
2023-10-03 14:23:56 +00:00
pEntity - > Teleport ( & vNewPos , NULL , NULL ) ;
2020-04-22 16:56:21 +00:00
# endif
2023-10-03 14:23:56 +00:00
}
return bWorked ;
2020-04-22 16:56:21 +00:00
}
//-----------------------------------------------------------------------------
2023-10-03 14:23:56 +00:00
// Purpose: Retrieves the MOD directory for the active game (ie. "hl2")
//-----------------------------------------------------------------------------
bool UTIL_GetModDir ( char * lpszTextOut , unsigned int nSize )
{
// Must pass in a buffer at least large enough to hold the desired string
const char * pGameDir = CommandLine ( ) - > ParmValue ( " -game " , " hl2 " ) ;
Assert ( strlen ( pGameDir ) < = nSize ) ;
if ( strlen ( pGameDir ) > nSize )
return false ;
Q_strncpy ( lpszTextOut , pGameDir , nSize ) ;
if ( Q_strnchr ( lpszTextOut , ' / ' , nSize ) | | Q_strnchr ( lpszTextOut , ' \\ ' , nSize ) )
{
// Strip the last directory off (which will be our game dir)
Q_StripLastDir ( lpszTextOut , nSize ) ;
// Find the difference in string lengths and take that difference from the original string as the mod dir
int dirlen = Q_strlen ( lpszTextOut ) ;
Q_strncpy ( lpszTextOut , pGameDir + dirlen , Q_strlen ( pGameDir ) - dirlen + 1 ) ;
}
return true ;
}
//#define FRUSTUM_DEBUGGING //for dumping some clipping information in UTIL_CalcFrustumThroughConvexPolygon
int UTIL_CalcFrustumThroughConvexPolygon ( const Vector * pPolyVertices , int iPolyVertCount , const Vector & vFrustumOrigin , const VPlane * pInputFrustumPlanes , int iInputFrustumPlanes , VPlane * pOutputFrustumPlanes , int iMaxOutputPlanes , int iPreserveCount )
{
Assert ( iPreserveCount < = iMaxOutputPlanes ) ;
Assert ( iPreserveCount < = iInputFrustumPlanes ) ;
if ( iPolyVertCount < 3 )
return 0 ;
# if defined( FRUSTUM_DEBUGGING )
//put case-specific debug logic here and it will propogate
const bool bDebugThisCall = ( iInputFrustumPlanes > 0 ) ;
const float fDisplayTime = 0.0f ;
# endif
int iMaxComplexity = iMaxOutputPlanes - iPreserveCount ;
Vector * pClippedVerts ;
int iClippedVertCount ;
if ( iInputFrustumPlanes > 0 )
{
//clip the polygon by the input frustum
int iAllocSize = iPolyVertCount + iInputFrustumPlanes ;
Vector * pWorkVerts [ 2 ] ;
pWorkVerts [ 0 ] = ( Vector * ) stackalloc ( sizeof ( Vector ) * iAllocSize * 2 ) ; //possible to add 1 point per cut, iPolyVertCount starting points, iInputFrustumPlaneCount cuts
pWorkVerts [ 1 ] = pWorkVerts [ 0 ] + iAllocSize ;
//clip by first plane and put output into pInVerts
iClippedVertCount = ClipPolyToPlane ( ( Vector * ) pPolyVertices , iPolyVertCount , pWorkVerts [ 0 ] , pInputFrustumPlanes [ 0 ] . m_Normal , pInputFrustumPlanes [ 0 ] . m_Dist , 0.01f ) ;
//clip by other planes and flipflop in and out pointers
for ( int i = 1 ; i ! = iInputFrustumPlanes ; + + i )
{
if ( iClippedVertCount < 3 )
return 0 ; //nothing left in the frustum
iClippedVertCount = ClipPolyToPlane ( pWorkVerts [ ( i & 1 ) ^ 1 ] , iClippedVertCount , pWorkVerts [ i & 1 ] , pInputFrustumPlanes [ i ] . m_Normal , pInputFrustumPlanes [ i ] . m_Dist , 0.01f ) ;
}
if ( iClippedVertCount < 3 )
return false ; //nothing left in the frustum
pClippedVerts = pWorkVerts [ ( iInputFrustumPlanes & 1 ) ^ 1 ] ;
}
else
{
//no input frustum
if ( iPolyVertCount > iMaxComplexity )
{
//we'll need to reduce our output frustum, copy the input polygon
pClippedVerts = ( Vector * ) stackalloc ( sizeof ( Vector ) * iPolyVertCount ) ;
memcpy ( pClippedVerts , pPolyVertices , sizeof ( Vector ) * iPolyVertCount ) ;
}
else
{
//we won't need to simplify the polygon to reduce output planes, just point at the input polygon
pClippedVerts = ( Vector * ) pPolyVertices ;
}
iClippedVertCount = iPolyVertCount ;
}
# if defined( FRUSTUM_DEBUGGING ) //for visibility culling debugging
if ( bDebugThisCall )
{
NDebugOverlay : : Line ( pClippedVerts [ iClippedVertCount - 1 ] , pClippedVerts [ 0 ] , 255 , 0 , 0 , true , fDisplayTime ) ;
for ( int j = 0 ; j ! = iClippedVertCount - 1 ; + + j )
{
NDebugOverlay : : Line ( pClippedVerts [ j ] , pClippedVerts [ j + 1 ] , 255 , 0 , 0 , true , fDisplayTime ) ;
}
}
# endif
Assert ( iClippedVertCount < = ( iPolyVertCount + iInputFrustumPlanes ) ) ;
if ( iClippedVertCount > iMaxComplexity )
{
# if defined( FRUSTUM_DEBUGGING )
if ( bDebugThisCall )
{
NDebugOverlay : : Line ( pClippedVerts [ iClippedVertCount - 1 ] , pClippedVerts [ 0 ] , 0 , 255 , 0 , false , fDisplayTime ) ;
for ( int j = 0 ; j ! = iClippedVertCount - 1 ; + + j )
{
NDebugOverlay : : Line ( pClippedVerts [ j ] , pClippedVerts [ j + 1 ] , 0 , 255 , 0 , true , fDisplayTime ) ;
}
}
# endif
float * fLineLengthSqr = ( float * ) stackalloc ( sizeof ( float ) * iClippedVertCount ) ;
for ( int i = 0 ; i ! = ( iClippedVertCount - 1 ) ; + + i )
{
fLineLengthSqr [ i ] = ( pClippedVerts [ i + 1 ] - pClippedVerts [ i ] ) . LengthSqr ( ) ;
}
fLineLengthSqr [ ( iClippedVertCount - 1 ) ] = ( pClippedVerts [ 0 ] - pClippedVerts [ ( iClippedVertCount - 1 ) ] ) . LengthSqr ( ) ; //wrap around
# if defined( FRUSTUM_DEBUGGING ) //for visibility culling debugging
Vector vDebugBoxExtent ;
vDebugBoxExtent . Init ( 1.0f , 1.0f , 1.0f ) ;
# endif
while ( iClippedVertCount > iMaxComplexity ) //vert count == number of planes we need to bound the polygon
{
//we have too many verts to represent this accurately in the output frustum plane count
//so, we're going to eliminate the smallest sides one at a time and bridge the surrounding sides until we're down to iMaxComplexity
float fMinSide = fLineLengthSqr [ 0 ] ;
int iMinSideFirstPoint = 0 ;
int iOldVertCount = iClippedVertCount ;
- - iClippedVertCount ; //we're going to decrement this sometime in this block, it makes math easier to do it now
for ( int i = 1 ; i ! = iOldVertCount ; + + i )
{
if ( fLineLengthSqr [ i ] < fMinSide )
{
fMinSide = fLineLengthSqr [ i ] ;
iMinSideFirstPoint = i ;
}
}
int i1 , i2 , i3 , i4 ;
i1 = ( iMinSideFirstPoint + iClippedVertCount ) % ( iOldVertCount ) ; //-1 with a wrap
i2 = iMinSideFirstPoint ;
i3 = ( iMinSideFirstPoint + 1 ) % ( iOldVertCount ) ;
i4 = ( iMinSideFirstPoint + 2 ) % ( iOldVertCount ) ;
Vector * p1 , * p2 , * p3 , * p4 ;
p1 = & pClippedVerts [ i1 ] ;
p2 = & pClippedVerts [ i2 ] ;
p3 = & pClippedVerts [ i3 ] ; //this is the one we'll actually be dropping in the merge
p4 = & pClippedVerts [ i4 ] ;
//now we know the two points that we have to merge to one, project and make a merged point from the surrounding lines
//if( fMinSide >= 0.1f ) //only worth doing the math if it's actually going to be accurate and make a difference
{
//http://mathworld.wolfram.com/Line-LineIntersection.html (20)
Vector vA = * p2 - * p1 ;
Vector vB = * p4 - * p3 ;
Vector vC = * p3 - * p1 ;
Vector vCxB = vC . Cross ( vB ) ;
Vector vAxB = vA . Cross ( vB ) ;
float fS = vCxB . Dot ( vAxB ) / vAxB . LengthSqr ( ) ;
* p2 = * p1 + ( vA * fS ) ;
fLineLengthSqr [ i1 ] = ( * p2 - * p1 ) . LengthSqr ( ) ;
}
fLineLengthSqr [ i2 ] = ( * p4 - * p2 ) . LengthSqr ( ) ; //must do this BEFORE possibly shifting points p4+ left
if ( i3 < i4 ) //not the last point in the array
{
int iElementShift = ( iOldVertCount - i4 ) ;
//eliminate p3, we merged p2+p3 and already stored the result in p2
memmove ( p3 , p4 , sizeof ( Vector ) * iElementShift ) ;
memmove ( & fLineLengthSqr [ i3 ] , & fLineLengthSqr [ i4 ] , sizeof ( float ) * iElementShift ) ;
}
}
# if defined(FRUSTUM_DEBUGGING) //for visibility culling debugging
if ( bDebugThisCall )
{
NDebugOverlay : : Line ( pClippedVerts [ iClippedVertCount - 1 ] , pClippedVerts [ 0 ] , 0 , 0 , 255 , false , fDisplayTime ) ;
for ( int j = 0 ; j ! = iClippedVertCount - 1 ; + + j )
{
NDebugOverlay : : Line ( pClippedVerts [ j ] , pClippedVerts [ j + 1 ] , 0 , 0 , 255 , true , fDisplayTime ) ;
}
}
# endif
}
//generate planes defined by each line around the convex and the frustum origin
{
int iFlipNormalsXOR = 0 ; //this algorithm was written assuming polygon vertices would be in a clockwise order from the perspective of vFrustumOrigin, some logic needs to flip if the inverse is true
{
Vector vLine1 = pPolyVertices [ 1 ] - pPolyVertices [ 0 ] ;
Vector vLine2 = pPolyVertices [ 2 ] - pPolyVertices [ 1 ] ;
Vector vFrontFace = vLine2 . Cross ( vLine1 ) ;
iFlipNormalsXOR = ( vFrontFace . Dot ( vFrustumOrigin - pPolyVertices [ 0 ] ) < 0.0f ) ? 1 : 0 ; //this will assist in reversing the normal by flipping the cross product
}
Vector vTemp [ 2 ] ;
vTemp [ 0 ] = pClippedVerts [ iClippedVertCount - 1 ] - vFrustumOrigin ;
for ( int i = 0 ; i ! = iClippedVertCount ; + + i )
{
int iIndexing = i & 1 ; //we can carry over the line computation from one iteration to the next, flip which order we look at the temps with
vTemp [ iIndexing ^ 1 ] = pClippedVerts [ i ] - vFrustumOrigin ;
Vector vNormal = vTemp [ iIndexing ^ iFlipNormalsXOR ] . Cross ( vTemp [ ( iIndexing ^ iFlipNormalsXOR ) ^ 1 ] ) ; //vLine1.Cross( vLine2 );
vNormal . NormalizeInPlace ( ) ;
pOutputFrustumPlanes [ i ] . Init ( vNormal , vNormal . Dot ( vFrustumOrigin ) ) ;
}
}
//preserve input planes on request
if ( iPreserveCount > 0 )
{
memcpy ( & pOutputFrustumPlanes [ iClippedVertCount ] , & pInputFrustumPlanes [ iInputFrustumPlanes - iPreserveCount ] , sizeof ( VPlane ) * iPreserveCount ) ;
}
return ( iClippedVertCount + iPreserveCount ) ;
}
//-----------------------------------------------------------------------------
// class CFlaggedEntitiesEnum
2020-04-22 16:56:21 +00:00
//-----------------------------------------------------------------------------
2023-10-03 14:23:56 +00:00
CFlaggedEntitiesEnum : : CFlaggedEntitiesEnum ( CBaseEntity * * pList , int listMax , int flagMask )
{
m_pList = pList ;
m_listMax = listMax ;
m_flagMask = flagMask ;
m_count = 0 ;
}
bool CFlaggedEntitiesEnum : : AddToList ( CBaseEntity * pEntity )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
if ( m_count > = m_listMax )
{
AssertMsgOnce ( 0 , " reached enumerated list limit. Increase limit, decrease radius, or make it so entity flags will work for you " ) ;
return false ;
}
m_pList [ m_count ] = pEntity ;
m_count + + ;
return true ;
}
2020-04-22 16:56:21 +00:00
2023-10-03 14:23:56 +00:00
IterationRetval_t CFlaggedEntitiesEnum : : EnumElement ( IHandleEntity * pHandleEntity )
{
# if defined( CLIENT_DLL )
IClientEntity * pClientEntity = cl_entitylist - > GetClientEntityFromHandle ( pHandleEntity - > GetRefEHandle ( ) ) ;
C_BaseEntity * pEntity = pClientEntity ? pClientEntity - > GetBaseEntity ( ) : NULL ;
2020-04-22 16:56:21 +00:00
# else
2023-10-03 14:23:56 +00:00
CBaseEntity * pEntity = gEntList . GetBaseEntity ( pHandleEntity - > GetRefEHandle ( ) ) ;
2020-04-22 16:56:21 +00:00
# endif
2023-10-03 14:23:56 +00:00
if ( pEntity )
{
if ( m_flagMask & & ! ( pEntity - > GetFlags ( ) & m_flagMask ) ) // Does it meet the criteria?
return ITERATION_CONTINUE ;
if ( ! AddToList ( pEntity ) )
return ITERATION_STOP ;
}
return ITERATION_CONTINUE ;
2020-04-22 16:56:21 +00:00
}
2023-10-03 14:23:56 +00:00
2020-04-22 16:56:21 +00:00
//-----------------------------------------------------------------------------
2023-10-03 14:23:56 +00:00
// class CHurtableEntitiesEnum
2020-04-22 16:56:21 +00:00
//-----------------------------------------------------------------------------
2023-10-03 14:23:56 +00:00
CHurtableEntitiesEnum : : CHurtableEntitiesEnum ( CBaseEntity * * pList , int listMax )
{
m_pList = pList ;
m_listMax = listMax ;
m_count = 0 ;
}
bool CHurtableEntitiesEnum : : AddToList ( CBaseEntity * pEntity )
{
if ( m_count > = m_listMax )
{
AssertMsgOnce ( 0 , " reached enumerated list limit. Increase limit, decrease radius, or make it so entity flags will work for you " ) ;
return false ;
}
m_pList [ m_count ] = pEntity ;
m_count + + ;
return true ;
}
IterationRetval_t CHurtableEntitiesEnum : : EnumElement ( IHandleEntity * pHandleEntity )
{
# if defined( CLIENT_DLL )
IClientEntity * pClientEntity = cl_entitylist - > GetClientEntityFromHandle ( pHandleEntity - > GetRefEHandle ( ) ) ;
C_BaseEntity * pEntity = pClientEntity ? pClientEntity - > GetBaseEntity ( ) : NULL ;
# else
CBaseEntity * pEntity = gEntList . GetBaseEntity ( pHandleEntity - > GetRefEHandle ( ) ) ;
# endif
if ( pEntity )
{
if ( ( pEntity - > m_takedamage = = DAMAGE_NO | | pEntity - > GetHealth ( ) < = 0 ) & & pEntity - > GetMoveType ( ) ! = MOVETYPE_VPHYSICS ) // Does it meet the criteria?
return ITERATION_CONTINUE ;
if ( ! AddToList ( pEntity ) )
return ITERATION_STOP ;
}
return ITERATION_CONTINUE ;
}
int UTIL_EntitiesAlongRay ( const Ray_t & ray , CFlaggedEntitiesEnum * pEnum )
2020-04-22 16:56:21 +00:00
{
2023-10-03 14:23:56 +00:00
# if defined( CLIENT_DLL )
partition - > EnumerateElementsAlongRay ( PARTITION_CLIENT_NON_STATIC_EDICTS , ray , false , pEnum ) ;
2020-04-22 16:56:21 +00:00
# else
2023-10-03 14:23:56 +00:00
partition - > EnumerateElementsAlongRay ( PARTITION_ENGINE_NON_STATIC_EDICTS , ray , false , pEnum ) ;
2020-04-22 16:56:21 +00:00
# endif
2023-10-03 14:23:56 +00:00
return pEnum - > GetCount ( ) ;
2020-04-22 16:56:21 +00:00
}