//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Utility code.
//
// $NoKeywords: $
//=============================================================================//
# include "cbase.h"
# include "saverestore.h"
# include "globalstate.h"
# include <stdarg.h>
# include "shake.h"
# include "decals.h"
# include "player.h"
# include "gamerules.h"
# include "entitylist.h"
# include "bspfile.h"
# include "mathlib/mathlib.h"
# include "IEffects.h"
# include "vstdlib/random.h"
# include "soundflags.h"
# include "ispatialpartition.h"
# include "igamesystem.h"
# include "saverestoretypes.h"
# include "checksum_crc.h"
# include "hierarchy.h"
# include "iservervehicle.h"
# include "te_effect_dispatch.h"
# include "utldict.h"
# include "collisionutils.h"
# include "movevars_shared.h"
# include "inetchannelinfo.h"
# include "tier0/vprof.h"
# include "ndebugoverlay.h"
# include "engine/ivdebugoverlay.h"
# include "datacache/imdlcache.h"
# include "util.h"
# include "cdll_int.h"
# ifdef PORTAL
# include "PortalSimulation.h"
//#include "Portal_PhysicsEnvironmentMgr.h"
# endif
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
extern short g_sModelIndexSmoke ; // (in combatweapon.cpp) holds the index for the smoke cloud
extern short g_sModelIndexBloodDrop ; // (in combatweapon.cpp) holds the sprite index for the initial blood
extern short g_sModelIndexBloodSpray ; // (in combatweapon.cpp) holds the sprite index for splattered blood
# ifdef DEBUG
void DBG_AssertFunction ( bool fExpr , const char * szExpr , const char * szFile , int szLine , const char * szMessage )
{
if ( fExpr )
return ;
char szOut [ 512 ] ;
if ( szMessage ! = NULL )
Q_snprintf ( szOut , sizeof ( szOut ) , " ASSERT FAILED: \n %s \n (%s@%d) \n %s " , szExpr , szFile , szLine , szMessage ) ;
else
Q_snprintf ( szOut , sizeof ( szOut ) , " ASSERT FAILED: \n %s \n (%s@%d) \n " , szExpr , szFile , szLine ) ;
Warning ( szOut ) ;
}
# endif // DEBUG
//-----------------------------------------------------------------------------
// Entity creation factory
//-----------------------------------------------------------------------------
class CEntityFactoryDictionary : public IEntityFactoryDictionary
{
public :
CEntityFactoryDictionary ( ) ;
virtual void InstallFactory ( IEntityFactory * pFactory , const char * pClassName ) ;
virtual IServerNetworkable * Create ( const char * pClassName ) ;
virtual void Destroy ( const char * pClassName , IServerNetworkable * pNetworkable ) ;
virtual const char * GetCannonicalName ( const char * pClassName ) ;
void ReportEntitySizes ( ) ;
private :
IEntityFactory * FindFactory ( const char * pClassName ) ;
public :
CUtlDict < IEntityFactory * , unsigned short > m_Factories ;
} ;
//-----------------------------------------------------------------------------
// Singleton accessor
//-----------------------------------------------------------------------------
IEntityFactoryDictionary * EntityFactoryDictionary ( )
{
static CEntityFactoryDictionary s_EntityFactory ;
return & s_EntityFactory ;
}
void DumpEntityFactories_f ( )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
CEntityFactoryDictionary * dict = ( CEntityFactoryDictionary * ) EntityFactoryDictionary ( ) ;
if ( dict )
{
for ( int i = dict - > m_Factories . First ( ) ; i ! = dict - > m_Factories . InvalidIndex ( ) ; i = dict - > m_Factories . Next ( i ) )
{
Warning ( " %s \n " , dict - > m_Factories . GetElementName ( i ) ) ;
}
}
}
static ConCommand dumpentityfactories ( " dumpentityfactories " , DumpEntityFactories_f , " Lists all entity factory names. " , FCVAR_GAMEDLL ) ;
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CON_COMMAND ( dump_entity_sizes , " Print sizeof(entclass) " )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
( ( CEntityFactoryDictionary * ) EntityFactoryDictionary ( ) ) - > ReportEntitySizes ( ) ;
}
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CEntityFactoryDictionary : : CEntityFactoryDictionary ( ) : m_Factories ( true , 0 , 128 )
{
}
//-----------------------------------------------------------------------------
// Finds a new factory
//-----------------------------------------------------------------------------
IEntityFactory * CEntityFactoryDictionary : : FindFactory ( const char * pClassName )
{
unsigned short nIndex = m_Factories . Find ( pClassName ) ;
if ( nIndex = = m_Factories . InvalidIndex ( ) )
return NULL ;
return m_Factories [ nIndex ] ;
}
//-----------------------------------------------------------------------------
// Install a new factory
//-----------------------------------------------------------------------------
void CEntityFactoryDictionary : : InstallFactory ( IEntityFactory * pFactory , const char * pClassName )
{
Assert ( FindFactory ( pClassName ) = = NULL ) ;
m_Factories . Insert ( pClassName , pFactory ) ;
}
//-----------------------------------------------------------------------------
// Instantiate something using a factory
//-----------------------------------------------------------------------------
IServerNetworkable * CEntityFactoryDictionary : : Create ( const char * pClassName )
{
IEntityFactory * pFactory = FindFactory ( pClassName ) ;
if ( ! pFactory )
{
Warning ( " Attempted to create unknown entity type %s! \n " , pClassName ) ;
return NULL ;
}
# if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG)
MEM_ALLOC_CREDIT_ ( m_Factories . GetElementName ( m_Factories . Find ( pClassName ) ) ) ;
# endif
return pFactory - > Create ( pClassName ) ;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const char * CEntityFactoryDictionary : : GetCannonicalName ( const char * pClassName )
{
return m_Factories . GetElementName ( m_Factories . Find ( pClassName ) ) ;
}
//-----------------------------------------------------------------------------
// Destroy a networkable
//-----------------------------------------------------------------------------
void CEntityFactoryDictionary : : Destroy ( const char * pClassName , IServerNetworkable * pNetworkable )
{
IEntityFactory * pFactory = FindFactory ( pClassName ) ;
if ( ! pFactory )
{
Warning ( " Attempted to destroy unknown entity type %s! \n " , pClassName ) ;
return ;
}
pFactory - > Destroy ( pNetworkable ) ;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CEntityFactoryDictionary : : ReportEntitySizes ( )
{
for ( int i = m_Factories . First ( ) ; i ! = m_Factories . InvalidIndex ( ) ; i = m_Factories . Next ( i ) )
{
Msg ( " %s: %d " , m_Factories . GetElementName ( i ) , m_Factories [ i ] - > GetEntitySize ( ) ) ;
}
}
//-----------------------------------------------------------------------------
// class CFlaggedEntitiesEnum
//-----------------------------------------------------------------------------
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 )
{
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 CFlaggedEntitiesEnum : : EnumElement ( IHandleEntity * pHandleEntity )
{
CBaseEntity * pEntity = gEntList . GetBaseEntity ( pHandleEntity - > GetRefEHandle ( ) ) ;
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 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int UTIL_PrecacheDecal ( const char * name , bool preload )
{
// If this is out of order, make sure to warn.
if ( ! CBaseEntity : : IsPrecacheAllowed ( ) )
{
if ( ! engine - > IsDecalPrecached ( name ) )
{
Assert ( ! " UTIL_PrecacheDecal: too late " ) ;
Warning ( " Late precache of %s \n " , name ) ;
}
}
return engine - > PrecacheDecal ( name , preload ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float UTIL_GetSimulationInterval ( )
{
if ( CBaseEntity : : IsSimulatingOnAlternateTicks ( ) )
return ( TICK_INTERVAL * 2.0 ) ;
return TICK_INTERVAL ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int UTIL_EntitiesInBox ( const Vector & mins , const Vector & maxs , CFlaggedEntitiesEnum * pEnum )
{
partition - > EnumerateElementsInBox ( PARTITION_ENGINE_NON_STATIC_EDICTS , mins , maxs , false , pEnum ) ;
return pEnum - > GetCount ( ) ;
}
int UTIL_EntitiesAlongRay ( const Ray_t & ray , CFlaggedEntitiesEnum * pEnum )
{
partition - > EnumerateElementsAlongRay ( PARTITION_ENGINE_NON_STATIC_EDICTS , ray , false , pEnum ) ;
return pEnum - > GetCount ( ) ;
}
int UTIL_EntitiesInSphere ( const Vector & center , float radius , CFlaggedEntitiesEnum * pEnum )
{
partition - > EnumerateElementsInSphere ( PARTITION_ENGINE_NON_STATIC_EDICTS , center , radius , false , pEnum ) ;
return pEnum - > GetCount ( ) ;
}
CEntitySphereQuery : : CEntitySphereQuery ( const Vector & center , float radius , int flagMask )
{
m_listIndex = 0 ;
m_listCount = UTIL_EntitiesInSphere ( m_pList , ARRAYSIZE ( m_pList ) , center , radius , flagMask ) ;
}
CBaseEntity * CEntitySphereQuery : : GetCurrentEntity ( )
{
if ( m_listIndex < m_listCount )
return m_pList [ m_listIndex ] ;
return NULL ;
}
//-----------------------------------------------------------------------------
// Simple trace filter
//-----------------------------------------------------------------------------
class CTracePassFilter : public CTraceFilter
{
public :
CTracePassFilter ( IHandleEntity * pPassEnt ) : m_pPassEnt ( pPassEnt ) { }
bool ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
if ( ! StandardFilterRules ( pHandleEntity , contentsMask ) )
return false ;
if ( ! PassServerEntityFilter ( pHandleEntity , m_pPassEnt ) )
return false ;
return true ;
}
private :
IHandleEntity * m_pPassEnt ;
} ;
//-----------------------------------------------------------------------------
// Drops an entity onto the floor
//-----------------------------------------------------------------------------
int UTIL_DropToFloor ( CBaseEntity * pEntity , unsigned int mask , CBaseEntity * pIgnore )
{
// Assume no ground
pEntity - > SetGroundEntity ( NULL ) ;
Assert ( pEntity ) ;
trace_t trace ;
# if !defined(HL2MP) && !defined(HL1_DLL)
// HACK: is this really the only sure way to detect crossing a terrain boundry?
UTIL_TraceEntity ( pEntity , pEntity - > GetAbsOrigin ( ) , pEntity - > GetAbsOrigin ( ) , mask , pIgnore , pEntity - > GetCollisionGroup ( ) , & trace ) ;
if ( trace . fraction = = 0.0 )
return - 1 ;
# endif // HL2MP
UTIL_TraceEntity ( pEntity , pEntity - > GetAbsOrigin ( ) , pEntity - > GetAbsOrigin ( ) - Vector ( 0 , 0 , 256 ) , mask , pIgnore , pEntity - > GetCollisionGroup ( ) , & trace ) ;
if ( trace . allsolid )
return - 1 ;
if ( trace . fraction = = 1 )
return 0 ;
pEntity - > SetAbsOrigin ( trace . endpos ) ;
pEntity - > SetGroundEntity ( trace . m_pEnt ) ;
return 1 ;
}
//-----------------------------------------------------------------------------
// Returns false if any part of the bottom of the entity is off an edge that
// is not a staircase.
//-----------------------------------------------------------------------------
bool UTIL_CheckBottom ( CBaseEntity * pEntity , ITraceFilter * pTraceFilter , float flStepSize )
{
Vector mins , maxs , start , stop ;
trace_t trace ;
int x , y ;
float mid , bottom ;
Assert ( pEntity ) ;
CTracePassFilter traceFilter ( pEntity ) ;
if ( ! pTraceFilter )
{
pTraceFilter = & traceFilter ;
}
unsigned int mask = pEntity - > PhysicsSolidMaskForEntity ( ) ;
VectorAdd ( pEntity - > GetAbsOrigin ( ) , pEntity - > WorldAlignMins ( ) , mins ) ;
VectorAdd ( pEntity - > GetAbsOrigin ( ) , pEntity - > WorldAlignMaxs ( ) , maxs ) ;
// if all of the points under the corners are solid world, don't bother
// with the tougher checks
// the corners must be within 16 of the midpoint
start [ 2 ] = mins [ 2 ] - 1 ;
for ( x = 0 ; x < = 1 ; x + + )
{
for ( y = 0 ; y < = 1 ; y + + )
{
start [ 0 ] = x ? maxs [ 0 ] : mins [ 0 ] ;
start [ 1 ] = y ? maxs [ 1 ] : mins [ 1 ] ;
if ( enginetrace - > GetPointContents ( start ) ! = CONTENTS_SOLID )
goto realcheck ;
}
}
return true ; // we got out easy
realcheck :
// check it for real...
start [ 2 ] = mins [ 2 ] + flStepSize ; // seems to help going up/down slopes.
// the midpoint must be within 16 of the bottom
start [ 0 ] = stop [ 0 ] = ( mins [ 0 ] + maxs [ 0 ] ) * 0.5 ;
start [ 1 ] = stop [ 1 ] = ( mins [ 1 ] + maxs [ 1 ] ) * 0.5 ;
stop [ 2 ] = start [ 2 ] - 2 * flStepSize ;
UTIL_TraceLine ( start , stop , mask , pTraceFilter , & trace ) ;
if ( trace . fraction = = 1.0 )
return false ;
mid = bottom = trace . endpos [ 2 ] ;
// the corners must be within 16 of the midpoint
for ( x = 0 ; x < = 1 ; x + + )
{
for ( y = 0 ; y < = 1 ; y + + )
{
start [ 0 ] = stop [ 0 ] = x ? maxs [ 0 ] : mins [ 0 ] ;
start [ 1 ] = stop [ 1 ] = y ? maxs [ 1 ] : mins [ 1 ] ;
UTIL_TraceLine ( start , stop , mask , pTraceFilter , & trace ) ;
if ( trace . fraction ! = 1.0 & & trace . endpos [ 2 ] > bottom )
bottom = trace . endpos [ 2 ] ;
if ( trace . fraction = = 1.0 | | mid - trace . endpos [ 2 ] > flStepSize )
return false ;
}
}
return true ;
}
bool g_bDisableEhandleAccess = false ;
bool g_bReceivedChainedUpdateOnRemove = false ;
//-----------------------------------------------------------------------------
// Purpose: Sets the entity up for deletion. Entity will not actually be deleted
// until the next frame, so there can be no pointer errors.
// Input : *oldObj - object to delete
//-----------------------------------------------------------------------------
void UTIL_Remove ( IServerNetworkable * oldObj )
{
CServerNetworkProperty * pProp = static_cast < CServerNetworkProperty * > ( oldObj ) ;
if ( ! pProp | | pProp - > IsMarkedForDeletion ( ) )
return ;
if ( PhysIsInCallback ( ) )
{
// This assert means that someone is deleting an entity inside a callback. That isn't supported so
// this code will defer the deletion of that object until the end of the current physics simulation frame
// Since this is hidden from the calling code it's preferred to call PhysCallbackRemove() directly from the caller
// in case the deferred delete will have unwanted results (like continuing to receive callbacks). That will make it
// obvious why the unwanted results are happening so the caller can handle them appropriately. (some callbacks can be masked
// or the calling entity can be flagged to filter them in most cases)
Assert ( 0 ) ;
PhysCallbackRemove ( oldObj ) ;
return ;
}
// mark it for deletion
pProp - > MarkForDeletion ( ) ;
CBaseEntity * pBaseEnt = oldObj - > GetBaseEntity ( ) ;
if ( pBaseEnt )
{
# ifdef PORTAL //make sure entities are in the primary physics environment for the portal mod, this code should be safe even if the entity is in neither extra environment
CPortalSimulator : : Pre_UTIL_Remove ( pBaseEnt ) ;
# endif
g_bReceivedChainedUpdateOnRemove = false ;
pBaseEnt - > UpdateOnRemove ( ) ;
Assert ( g_bReceivedChainedUpdateOnRemove ) ;
// clear oldObj targetname / other flags now
pBaseEnt - > SetName ( NULL_STRING ) ;
# ifdef PORTAL
CPortalSimulator : : Post_UTIL_Remove ( pBaseEnt ) ;
# endif
}
gEntList . AddToDeleteList ( oldObj ) ;
}
void UTIL_Remove ( CBaseEntity * oldObj )
{
if ( ! oldObj )
return ;
UTIL_Remove ( oldObj - > NetworkProp ( ) ) ;
}
static int s_RemoveImmediateSemaphore = 0 ;
void UTIL_DisableRemoveImmediate ( )
{
s_RemoveImmediateSemaphore + + ;
}
void UTIL_EnableRemoveImmediate ( )
{
s_RemoveImmediateSemaphore - - ;
Assert ( s_RemoveImmediateSemaphore > = 0 ) ;
}
//-----------------------------------------------------------------------------
// Purpose: deletes an entity, without any delay. WARNING! Only use this when sure
// no pointers rely on this entity.
// Input : *oldObj - the entity to delete
//-----------------------------------------------------------------------------
void UTIL_RemoveImmediate ( CBaseEntity * oldObj )
{
// valid pointer or already removed?
if ( ! oldObj | | oldObj - > IsEFlagSet ( EFL_KILLME ) )
return ;
if ( s_RemoveImmediateSemaphore )
{
UTIL_Remove ( oldObj ) ;
return ;
}
# ifdef PORTAL //make sure entities are in the primary physics environment for the portal mod, this code should be safe even if the entity is in neither extra environment
CPortalSimulator : : Pre_UTIL_Remove ( oldObj ) ;
# endif
oldObj - > AddEFlags ( EFL_KILLME ) ; // Make sure to ignore further calls into here or UTIL_Remove.
g_bReceivedChainedUpdateOnRemove = false ;
oldObj - > UpdateOnRemove ( ) ;
Assert ( g_bReceivedChainedUpdateOnRemove ) ;
// Entities shouldn't reference other entities in their destructors
// that type of code should only occur in an UpdateOnRemove call
g_bDisableEhandleAccess = true ;
delete oldObj ;
g_bDisableEhandleAccess = false ;
# ifdef PORTAL
CPortalSimulator : : Post_UTIL_Remove ( oldObj ) ;
# endif
}
// returns a CBaseEntity pointer to a player by index. Only returns if the player is spawned and connected
// otherwise returns NULL
// Index is 1 based
CBasePlayer * UTIL_PlayerByIndex ( int playerIndex )
{
CBasePlayer * pPlayer = NULL ;
if ( playerIndex > 0 & & playerIndex < = gpGlobals - > maxClients )
{
edict_t * pPlayerEdict = INDEXENT ( playerIndex ) ;
if ( pPlayerEdict & & ! pPlayerEdict - > IsFree ( ) )
{
pPlayer = ( CBasePlayer * ) GetContainingEntity ( pPlayerEdict ) ;
}
}
return pPlayer ;
}
CBasePlayer * UTIL_PlayerByName ( const char * name )
{
if ( ! name | | ! name [ 0 ] )
return NULL ;
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBasePlayer * pPlayer = UTIL_PlayerByIndex ( i ) ;
if ( ! pPlayer )
continue ;
if ( ! pPlayer - > IsConnected ( ) )
continue ;
if ( Q_stricmp ( pPlayer - > GetPlayerName ( ) , name ) = = 0 )
{
return pPlayer ;
}
}
return NULL ;
}
CBasePlayer * UTIL_PlayerByUserId ( int userID )
{
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBasePlayer * pPlayer = UTIL_PlayerByIndex ( i ) ;
if ( ! pPlayer )
continue ;
if ( ! pPlayer - > IsConnected ( ) )
continue ;
if ( engine - > GetPlayerUserId ( pPlayer - > edict ( ) ) = = userID )
{
return pPlayer ;
}
}
return NULL ;
}
//
// Return the local player.
// If this is a multiplayer game, return NULL.
//
CBasePlayer * UTIL_GetLocalPlayer ( void )
{
if ( gpGlobals - > maxClients > 1 )
{
if ( developer . GetBool ( ) )
{
Assert ( ! " UTIL_GetLocalPlayer " ) ;
# ifdef DEBUG
Warning ( " UTIL_GetLocalPlayer() called in multiplayer game. \n " ) ;
# endif
}
return NULL ;
}
return UTIL_PlayerByIndex ( 1 ) ;
}
//
// Get the local player on a listen server - this is for multiplayer use only
//
CBasePlayer * UTIL_GetListenServerHost ( void )
{
// no "local player" if this is a dedicated server or a single player game
if ( engine - > IsDedicatedServer ( ) )
{
Assert ( ! " UTIL_GetListenServerHost " ) ;
Warning ( " UTIL_GetListenServerHost() called from a dedicated server or single-player game. \n " ) ;
return NULL ;
}
return UTIL_PlayerByIndex ( 1 ) ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Returns true if the command was issued by the listenserver host , or by the dedicated server , via rcon or the server console .
* This is valid during ConCommand execution .
*/
bool UTIL_IsCommandIssuedByServerAdmin ( void )
{
int issuingPlayerIndex = UTIL_GetCommandClientIndex ( ) ;
if ( engine - > IsDedicatedServer ( ) & & issuingPlayerIndex > 0 )
return false ;
# if defined( REPLAY_ENABLED )
// entity 1 is replay?
player_info_t pi ;
bool bPlayerIsReplay = engine - > GetPlayerInfo ( 1 , & pi ) & & pi . isreplay ;
# else
bool bPlayerIsReplay = false ;
# endif
if ( bPlayerIsReplay )
{
if ( issuingPlayerIndex > 2 )
return false ;
}
else if ( issuingPlayerIndex > 1 )
{
return false ;
}
return true ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Returns a CBaseEntity pointer by entindex . Index is 1 based .
*/
CBaseEntity * UTIL_EntityByIndex ( int entityIndex )
{
CBaseEntity * entity = NULL ;
if ( entityIndex > 0 )
{
edict_t * edict = INDEXENT ( entityIndex ) ;
if ( edict & & ! edict - > IsFree ( ) )
{
entity = GetContainingEntity ( edict ) ;
}
}
return entity ;
}
int ENTINDEX ( CBaseEntity * pEnt )
{
// This works just like ENTINDEX for edicts.
if ( pEnt )
return pEnt - > entindex ( ) ;
else
return 0 ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : playerIndex -
// ping -
// packetloss -
//-----------------------------------------------------------------------------
void UTIL_GetPlayerConnectionInfo ( int playerIndex , int & ping , int & packetloss )
{
CBasePlayer * player = UTIL_PlayerByIndex ( playerIndex ) ;
INetChannelInfo * nci = engine - > GetPlayerNetInfo ( playerIndex ) ;
if ( nci & & player & & ! player - > IsBot ( ) )
{
float latency = nci - > GetAvgLatency ( FLOW_OUTGOING ) ; // in seconds
// that should be the correct latency, we assume that cmdrate is higher
// then updaterate, what is the case for default settings
const char * szCmdRate = engine - > GetClientConVarValue ( playerIndex , " cl_cmdrate " ) ;
int nCmdRate = MAX ( 1 , Q_atoi ( szCmdRate ) ) ;
latency - = ( 0.5f / nCmdRate ) + TICKS_TO_TIME ( 1.0f ) ; // correct latency
// in GoldSrc we had a different, not fixed tickrate. so we have to adjust
// Source pings by half a tick to match the old GoldSrc pings.
latency - = TICKS_TO_TIME ( 0.5f ) ;
ping = latency * 1000.0f ; // as msecs
ping = clamp ( ping , 5 , 1000 ) ; // set bounds, dont show pings under 5 msecs
packetloss = 100.0f * nci - > GetAvgLoss ( FLOW_INCOMING ) ; // loss in percentage
packetloss = clamp ( packetloss , 0 , 100 ) ;
}
else
{
ping = 0 ;
packetloss = 0 ;
}
}
static unsigned short FixedUnsigned16 ( float value , float scale )
{
int output ;
output = value * scale ;
if ( output < 0 )
output = 0 ;
if ( output > 0xFFFF )
output = 0xFFFF ;
return ( unsigned short ) output ;
}
//-----------------------------------------------------------------------------
// Compute shake amplitude
//-----------------------------------------------------------------------------
inline float ComputeShakeAmplitude ( const Vector & center , const Vector & shakePt , float amplitude , float radius )
{
if ( radius < = 0 )
return amplitude ;
float localAmplitude = - 1 ;
Vector delta = center - shakePt ;
float distance = delta . Length ( ) ;
if ( distance < = radius )
{
// Make the amplitude fall off over distance
float flPerc = 1.0 - ( distance / radius ) ;
localAmplitude = amplitude * flPerc ;
}
return localAmplitude ;
}
//-----------------------------------------------------------------------------
// Transmits the actual shake event
//-----------------------------------------------------------------------------
inline void TransmitShakeEvent ( CBasePlayer * pPlayer , float localAmplitude , float frequency , float duration , ShakeCommand_t eCommand )
{
if ( ( localAmplitude > 0 ) | | ( eCommand = = SHAKE_STOP ) )
{
if ( eCommand = = SHAKE_STOP )
localAmplitude = 0 ;
CSingleUserRecipientFilter user ( pPlayer ) ;
user . MakeReliable ( ) ;
UserMessageBegin ( user , " Shake " ) ;
WRITE_BYTE ( eCommand ) ; // shake command (SHAKE_START, STOP, FREQUENCY, AMPLITUDE)
WRITE_FLOAT ( localAmplitude ) ; // shake magnitude/amplitude
WRITE_FLOAT ( frequency ) ; // shake noise frequency
WRITE_FLOAT ( duration ) ; // shake lasts this long
MessageEnd ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Shake the screen of all clients within radius.
// radius == 0, shake all clients
// UNDONE: Fix falloff model (disabled)?
// UNDONE: Affect user controls?
// Input : center - Center of screen shake, radius is measured from here.
// amplitude - Amplitude of shake
// frequency -
// duration - duration of shake in seconds.
// radius - Radius of effect, 0 shakes all clients.
// command - One of the following values:
// SHAKE_START - starts the screen shake for all players within the radius
// SHAKE_STOP - stops the screen shake for all players within the radius
// SHAKE_AMPLITUDE - modifies the amplitude of the screen shake
// for all players within the radius
// SHAKE_FREQUENCY - modifies the frequency of the screen shake
// for all players within the radius
// bAirShake - if this is false, then it will only shake players standing on the ground.
//-----------------------------------------------------------------------------
const float MAX_SHAKE_AMPLITUDE = 16.0f ;
void UTIL_ScreenShake ( const Vector & center , float amplitude , float frequency , float duration , float radius , ShakeCommand_t eCommand , bool bAirShake )
{
int i ;
float localAmplitude ;
if ( amplitude > MAX_SHAKE_AMPLITUDE )
{
amplitude = MAX_SHAKE_AMPLITUDE ;
}
for ( i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBaseEntity * pPlayer = UTIL_PlayerByIndex ( i ) ;
//
// Only start shakes for players that are on the ground unless doing an air shake.
//
if ( ! pPlayer | | ( ! bAirShake & & ( eCommand = = SHAKE_START ) & & ! ( pPlayer - > GetFlags ( ) & FL_ONGROUND ) ) )
{
continue ;
}
localAmplitude = ComputeShakeAmplitude ( center , pPlayer - > WorldSpaceCenter ( ) , amplitude , radius ) ;
// This happens if the player is outside the radius, in which case we should ignore
// all commands
if ( localAmplitude < 0 )
continue ;
TransmitShakeEvent ( ( CBasePlayer * ) pPlayer , localAmplitude , frequency , duration , eCommand ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Shake an object and all players on or near it
//-----------------------------------------------------------------------------
void UTIL_ScreenShakeObject ( CBaseEntity * pEnt , const Vector & center , float amplitude , float frequency , float duration , float radius , ShakeCommand_t eCommand , bool bAirShake )
{
int i ;
float localAmplitude ;
CBaseEntity * pHighestParent = pEnt - > GetRootMoveParent ( ) ;
for ( i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBaseEntity * pPlayer = UTIL_PlayerByIndex ( i ) ;
if ( ! pPlayer )
continue ;
// Shake the object, or anything hierarchically attached to it at maximum amplitude
localAmplitude = 0 ;
if ( pHighestParent = = pPlayer - > GetRootMoveParent ( ) )
{
localAmplitude = amplitude ;
}
else if ( ( pPlayer - > GetFlags ( ) & FL_ONGROUND ) & & ( pPlayer - > GetGroundEntity ( ) - > GetRootMoveParent ( ) = = pHighestParent ) )
{
// If the player is standing on the object, use maximum amplitude
localAmplitude = amplitude ;
}
else
{
// Only shake players that are on the ground.
if ( ! bAirShake & & ! ( pPlayer - > GetFlags ( ) & FL_ONGROUND ) )
{
continue ;
}
if ( radius > 0 )
{
localAmplitude = ComputeShakeAmplitude ( center , pPlayer - > WorldSpaceCenter ( ) , amplitude , radius ) ;
}
else
{
// If using a 0 radius, apply to everyone with no falloff
localAmplitude = amplitude ;
}
// This happens if the player is outside the radius,
// in which case we should ignore all commands
if ( localAmplitude < 0 )
continue ;
}
TransmitShakeEvent ( ( CBasePlayer * ) pPlayer , localAmplitude , frequency , duration , eCommand ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Punches the view of all clients within radius.
// If radius is 0, punches all clients.
// Input : center - Center of punch, radius is measured from here.
// radius - Radius of effect, 0 punches all clients.
// bInAir - if this is false, then it will only punch players standing on the ground.
//-----------------------------------------------------------------------------
void UTIL_ViewPunch ( const Vector & center , QAngle angPunch , float radius , bool bInAir )
{
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBaseEntity * pPlayer = UTIL_PlayerByIndex ( i ) ;
//
// Only apply the punch to players that are on the ground unless doing an air punch.
//
if ( ! pPlayer | | ( ! bInAir & & ! ( pPlayer - > GetFlags ( ) & FL_ONGROUND ) ) )
{
continue ;
}
QAngle angTemp = angPunch ;
if ( radius > 0 )
{
Vector delta = center - pPlayer - > GetAbsOrigin ( ) ;
float distance = delta . Length ( ) ;
if ( distance < = radius )
{
// Make the punch amplitude fall off over distance.
float flPerc = 1.0 - ( distance / radius ) ;
angTemp * = flPerc ;
}
else
{
continue ;
}
}
pPlayer - > ViewPunch ( angTemp ) ;
}
}
void UTIL_ScreenFadeBuild ( ScreenFade_t & fade , const color32 & color , float fadeTime , float fadeHold , int flags )
{
fade . duration = FixedUnsigned16 ( fadeTime , 1 < < SCREENFADE_FRACBITS ) ; // 7.9 fixed
fade . holdTime = FixedUnsigned16 ( fadeHold , 1 < < SCREENFADE_FRACBITS ) ; // 7.9 fixed
fade . r = color . r ;
fade . g = color . g ;
fade . b = color . b ;
fade . a = color . a ;
fade . fadeFlags = flags ;
}
void UTIL_ScreenFadeWrite ( const ScreenFade_t & fade , CBaseEntity * pEntity )
{
if ( ! pEntity | | ! pEntity - > IsNetClient ( ) )
return ;
CSingleUserRecipientFilter user ( ( CBasePlayer * ) pEntity ) ;
user . MakeReliable ( ) ;
UserMessageBegin ( user , " Fade " ) ; // use the magic #1 for "one client"
WRITE_SHORT ( fade . duration ) ; // fade lasts this long
WRITE_SHORT ( fade . holdTime ) ; // fade lasts this long
WRITE_SHORT ( fade . fadeFlags ) ; // fade type (in / out)
WRITE_BYTE ( fade . r ) ; // fade red
WRITE_BYTE ( fade . g ) ; // fade green
WRITE_BYTE ( fade . b ) ; // fade blue
WRITE_BYTE ( fade . a ) ; // fade blue
MessageEnd ( ) ;
}
void UTIL_ScreenFadeAll ( const color32 & color , float fadeTime , float fadeHold , int flags )
{
int i ;
ScreenFade_t fade ;
UTIL_ScreenFadeBuild ( fade , color , fadeTime , fadeHold , flags ) ;
for ( i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBaseEntity * pPlayer = UTIL_PlayerByIndex ( i ) ;
UTIL_ScreenFadeWrite ( fade , pPlayer ) ;
}
}
void UTIL_ScreenFade ( CBaseEntity * pEntity , const color32 & color , float fadeTime , float fadeHold , int flags )
{
ScreenFade_t fade ;
UTIL_ScreenFadeBuild ( fade , color , fadeTime , fadeHold , flags ) ;
UTIL_ScreenFadeWrite ( fade , pEntity ) ;
}
void UTIL_HudMessage ( CBasePlayer * pToPlayer , const hudtextparms_t & textparms , const char * pMessage )
{
CRecipientFilter filter ;
if ( pToPlayer )
{
filter . AddRecipient ( pToPlayer ) ;
}
else
{
filter . AddAllPlayers ( ) ;
}
filter . MakeReliable ( ) ;
UserMessageBegin ( filter , " HudMsg " ) ;
WRITE_BYTE ( textparms . channel & 0xFF ) ;
WRITE_FLOAT ( textparms . x ) ;
WRITE_FLOAT ( textparms . y ) ;
WRITE_BYTE ( textparms . r1 ) ;
WRITE_BYTE ( textparms . g1 ) ;
WRITE_BYTE ( textparms . b1 ) ;
WRITE_BYTE ( textparms . a1 ) ;
WRITE_BYTE ( textparms . r2 ) ;
WRITE_BYTE ( textparms . g2 ) ;
WRITE_BYTE ( textparms . b2 ) ;
WRITE_BYTE ( textparms . a2 ) ;
WRITE_BYTE ( textparms . effect ) ;
WRITE_FLOAT ( textparms . fadeinTime ) ;
WRITE_FLOAT ( textparms . fadeoutTime ) ;
WRITE_FLOAT ( textparms . holdTime ) ;
WRITE_FLOAT ( textparms . fxTime ) ;
WRITE_STRING ( pMessage ) ;
MessageEnd ( ) ;
}
void UTIL_HudMessageAll ( const hudtextparms_t & textparms , const char * pMessage )
{
UTIL_HudMessage ( NULL , textparms , pMessage ) ;
}
void UTIL_HudHintText ( CBaseEntity * pEntity , const char * pMessage )
{
if ( ! pEntity )
return ;
CSingleUserRecipientFilter user ( ( CBasePlayer * ) pEntity ) ;
user . MakeReliable ( ) ;
UserMessageBegin ( user , " KeyHintText " ) ;
WRITE_BYTE ( 1 ) ; // one string
WRITE_STRING ( pMessage ) ;
MessageEnd ( ) ;
}
void UTIL_ClientPrintFilter ( IRecipientFilter & filter , int msg_dest , const char * msg_name , const char * param1 , const char * param2 , const char * param3 , const char * param4 )
{
UserMessageBegin ( filter , " TextMsg " ) ;
WRITE_BYTE ( msg_dest ) ;
WRITE_STRING ( msg_name ) ;
if ( param1 )
WRITE_STRING ( param1 ) ;
else
WRITE_STRING ( " " ) ;
if ( param2 )
WRITE_STRING ( param2 ) ;
else
WRITE_STRING ( " " ) ;
if ( param3 )
WRITE_STRING ( param3 ) ;
else
WRITE_STRING ( " " ) ;
if ( param4 )
WRITE_STRING ( param4 ) ;
else
WRITE_STRING ( " " ) ;
MessageEnd ( ) ;
}
void UTIL_ClientPrintAll ( int msg_dest , const char * msg_name , const char * param1 , const char * param2 , const char * param3 , const char * param4 )
{
CReliableBroadcastRecipientFilter filter ;
UTIL_ClientPrintFilter ( filter , msg_dest , msg_name , param1 , param2 , param3 , param4 ) ;
}
void ClientPrint ( CBasePlayer * player , int msg_dest , const char * msg_name , const char * param1 , const char * param2 , const char * param3 , const char * param4 )
{
if ( ! player )
return ;
CSingleUserRecipientFilter user ( player ) ;
user . MakeReliable ( ) ;
UTIL_ClientPrintFilter ( user , msg_dest , msg_name , param1 , param2 , param3 , param4 ) ;
}
void UTIL_SayTextFilter ( IRecipientFilter & filter , const char * pText , CBasePlayer * pPlayer , bool bChat )
{
UserMessageBegin ( filter , " SayText " ) ;
if ( pPlayer )
{
WRITE_BYTE ( pPlayer - > entindex ( ) ) ;
}
else
{
WRITE_BYTE ( 0 ) ; // world, dedicated server says
}
WRITE_STRING ( pText ) ;
WRITE_BYTE ( bChat ) ;
MessageEnd ( ) ;
}
void UTIL_SayText2Filter ( IRecipientFilter & filter , CBasePlayer * pEntity , bool bChat , const char * msg_name , const char * param1 , const char * param2 , const char * param3 , const char * param4 )
{
UserMessageBegin ( filter , " SayText2 " ) ;
if ( pEntity )
{
WRITE_BYTE ( pEntity - > entindex ( ) ) ;
}
else
{
WRITE_BYTE ( 0 ) ; // world, dedicated server says
}
WRITE_BYTE ( bChat ) ;
WRITE_STRING ( msg_name ) ;
if ( param1 )
WRITE_STRING ( param1 ) ;
else
WRITE_STRING ( " " ) ;
if ( param2 )
WRITE_STRING ( param2 ) ;
else
WRITE_STRING ( " " ) ;
if ( param3 )
WRITE_STRING ( param3 ) ;
else
WRITE_STRING ( " " ) ;
if ( param4 )
WRITE_STRING ( param4 ) ;
else
WRITE_STRING ( " " ) ;
MessageEnd ( ) ;
}
void UTIL_SayText ( const char * pText , CBasePlayer * pToPlayer )
{
if ( ! pToPlayer - > IsNetClient ( ) )
return ;
CSingleUserRecipientFilter user ( pToPlayer ) ;
user . MakeReliable ( ) ;
UTIL_SayTextFilter ( user , pText , pToPlayer , false ) ;
}
void UTIL_SayTextAll ( const char * pText , CBasePlayer * pPlayer , bool bChat )
{
CReliableBroadcastRecipientFilter filter ;
UTIL_SayTextFilter ( filter , pText , pPlayer , bChat ) ;
}
void UTIL_ShowMessage ( const char * pString , CBasePlayer * pPlayer )
{
CRecipientFilter filter ;
if ( pPlayer )
{
filter . AddRecipient ( pPlayer ) ;
}
else
{
filter . AddAllPlayers ( ) ;
}
filter . MakeReliable ( ) ;
UserMessageBegin ( filter , " HudText " ) ;
WRITE_STRING ( pString ) ;
MessageEnd ( ) ;
}
void UTIL_ShowMessageAll ( const char * pString )
{
UTIL_ShowMessage ( pString , NULL ) ;
}
// So we always return a valid surface
static csurface_t g_NullSurface = { " **empty** " , 0 } ;
void UTIL_SetTrace ( trace_t & trace , const Ray_t & ray , edict_t * ent , float fraction ,
int hitgroup , unsigned int contents , const Vector & normal , float intercept )
{
trace . startsolid = ( fraction = = 0.0f ) ;
trace . fraction = fraction ;
VectorCopy ( ray . m_Start , trace . startpos ) ;
VectorMA ( ray . m_Start , fraction , ray . m_Delta , trace . endpos ) ;
VectorCopy ( normal , trace . plane . normal ) ;
trace . plane . dist = intercept ;
trace . m_pEnt = CBaseEntity : : Instance ( ent ) ;
trace . hitgroup = hitgroup ;
trace . surface = g_NullSurface ;
trace . contents = contents ;
}
void UTIL_ClearTrace ( trace_t & trace )
{
memset ( & trace , 0 , sizeof ( trace ) ) ;
trace . fraction = 1.f ;
trace . fractionleftsolid = 0 ;
trace . surface = g_NullSurface ;
}
//-----------------------------------------------------------------------------
// Sets the entity size
//-----------------------------------------------------------------------------
static void SetMinMaxSize ( CBaseEntity * pEnt , const Vector & mins , const Vector & maxs )
{
for ( int i = 0 ; i < 3 ; i + + )
{
if ( mins [ i ] > maxs [ i ] )
{
Error ( " %s: backwards mins/maxs " , ( pEnt ) ? pEnt - > GetDebugName ( ) : " <NULL> " ) ;
}
}
Assert ( pEnt ) ;
pEnt - > SetCollisionBounds ( mins , maxs ) ;
}
//-----------------------------------------------------------------------------
// Sets the model size
//-----------------------------------------------------------------------------
void UTIL_SetSize ( CBaseEntity * pEnt , const Vector & vecMin , const Vector & vecMax )
{
SetMinMaxSize ( pEnt , vecMin , vecMax ) ;
}
//-----------------------------------------------------------------------------
// Sets the model to be associated with an entity
//-----------------------------------------------------------------------------
void UTIL_SetModel ( CBaseEntity * pEntity , const char * pModelName )
{
// check to see if model was properly precached
int i = modelinfo - > GetModelIndex ( pModelName ) ;
if ( i = = - 1 )
{
Error ( " %i/%s - %s: UTIL_SetModel: not precached: %s \n " , pEntity - > entindex ( ) ,
STRING ( pEntity - > GetEntityName ( ) ) ,
pEntity - > GetClassname ( ) , pModelName ) ;
}
CBaseAnimating * pAnimating = pEntity - > GetBaseAnimating ( ) ;
if ( pAnimating )
{
pAnimating - > m_nForceBone = 0 ;
}
pEntity - > SetModelName ( AllocPooledString ( pModelName ) ) ;
pEntity - > SetModelIndex ( i ) ;
SetMinMaxSize ( pEntity , vec3_origin , vec3_origin ) ;
pEntity - > SetCollisionBoundsFromModel ( ) ;
}
void UTIL_SetOrigin ( CBaseEntity * entity , const Vector & vecOrigin , bool bFireTriggers )
{
entity - > SetLocalOrigin ( vecOrigin ) ;
if ( bFireTriggers )
{
entity - > PhysicsTouchTriggers ( ) ;
}
}
void UTIL_ParticleEffect ( const Vector & vecOrigin , const Vector & vecDirection , ULONG ulColor , ULONG ulCount )
{
Msg ( " UTIL_ParticleEffect: Disabled \n " ) ;
}
void UTIL_Smoke ( const Vector & origin , const float scale , const float framerate )
{
g_pEffects - > Smoke ( origin , g_sModelIndexSmoke , scale , framerate ) ;
}
// snaps a vector to the nearest axis vector (if within epsilon)
void UTIL_SnapDirectionToAxis ( Vector & direction , float epsilon )
{
float proj = 1 - epsilon ;
for ( int i = 0 ; i < 3 ; i + + )
{
if ( fabs ( direction [ i ] ) > proj )
{
// snap to axis unit vector
if ( direction [ i ] < 0 )
direction [ i ] = - 1.0f ;
else
direction [ i ] = 1.0f ;
direction [ ( i + 1 ) % 3 ] = 0 ;
direction [ ( i + 2 ) % 3 ] = 0 ;
return ;
}
}
}
char * UTIL_VarArgs ( const char * format , . . . )
{
va_list argptr ;
static char string [ 1024 ] ;
va_start ( argptr , format ) ;
Q_vsnprintf ( string , sizeof ( string ) , format , argptr ) ;
va_end ( argptr ) ;
return string ;
}
bool UTIL_IsMasterTriggered ( string_t sMaster , CBaseEntity * pActivator )
{
if ( sMaster ! = NULL_STRING )
{
CBaseEntity * pMaster = gEntList . FindEntityByName ( NULL , sMaster , NULL , pActivator ) ;
if ( pMaster & & ( pMaster - > ObjectCaps ( ) & FCAP_MASTER ) )
{
return pMaster - > IsTriggered ( pActivator ) ;
}
Warning ( " Master was null or not a master! \n " ) ;
}
// if this isn't a master entity, just say yes.
return true ;
}
void UTIL_BloodStream ( const Vector & origin , const Vector & direction , int color , int amount )
{
if ( ! UTIL_ShouldShowBlood ( color ) )
return ;
if ( g_Language . GetInt ( ) = = LANGUAGE_GERMAN & & color = = BLOOD_COLOR_RED )
color = 0 ;
CPVSFilter filter ( origin ) ;
te - > BloodStream ( filter , 0.0 , & origin , & direction , 247 , 63 , 14 , 255 , MIN ( amount , 255 ) ) ;
}
Vector UTIL_RandomBloodVector ( void )
{
Vector direction ;
direction . x = random - > RandomFloat ( - 1 , 1 ) ;
direction . y = random - > RandomFloat ( - 1 , 1 ) ;
direction . z = random - > RandomFloat ( 0 , 1 ) ;
return direction ;
}
//------------------------------------------------------------------------------
// Purpose : Creates both an decal and any associated impact effects (such
// as flecks) for the given iDamageType and the trace's end position
// Input :
// Output :
//------------------------------------------------------------------------------
void UTIL_ImpactTrace ( trace_t * pTrace , int iDamageType , const char * pCustomImpactName )
{
CBaseEntity * pEntity = pTrace - > m_pEnt ;
// Is the entity valid, is the surface sky?
if ( ! pEntity | | ! UTIL_IsValidEntity ( pEntity ) | | ( pTrace - > surface . flags & SURF_SKY ) )
return ;
if ( pTrace - > fraction = = 1.0 )
return ;
pEntity - > ImpactTrace ( pTrace , iDamageType , pCustomImpactName ) ;
}
/*
= = = = = = = = = = = = = =
UTIL_PlayerDecalTrace
A player is trying to apply his custom decal for the spray can .
Tell connected clients to display it , or use the default spray can decal
if the custom can ' t be loaded .
= = = = = = = = = = = = = =
*/
void UTIL_PlayerDecalTrace ( trace_t * pTrace , int playernum )
{
if ( pTrace - > fraction = = 1.0 )
return ;
CBroadcastRecipientFilter filter ;
te - > PlayerDecal ( filter , 0.0 ,
& pTrace - > endpos , playernum , pTrace - > m_pEnt - > entindex ( ) ) ;
}
bool UTIL_TeamsMatch ( const char * pTeamName1 , const char * pTeamName2 )
{
// Everyone matches unless it's teamplay
if ( ! g_pGameRules - > IsTeamplay ( ) )
return true ;
// Both on a team?
if ( * pTeamName1 ! = 0 & & * pTeamName2 ! = 0 )
{
if ( ! stricmp ( pTeamName1 , pTeamName2 ) ) // Same Team?
return true ;
}
return false ;
}
void UTIL_AxisStringToPointPoint ( Vector & start , Vector & end , const char * pString )
{
char tmpstr [ 256 ] ;
Q_strncpy ( tmpstr , pString , sizeof ( tmpstr ) ) ;
char * pVec = strtok ( tmpstr , " , " ) ;
int i = 0 ;
while ( pVec ! = NULL & & * pVec )
{
if ( i = = 0 )
{
UTIL_StringToVector ( start . Base ( ) , pVec ) ;
i + + ;
}
else
{
UTIL_StringToVector ( end . Base ( ) , pVec ) ;
}
pVec = strtok ( NULL , " , " ) ;
}
}
void UTIL_AxisStringToPointDir ( Vector & start , Vector & dir , const char * pString )
{
Vector end ;
UTIL_AxisStringToPointPoint ( start , end , pString ) ;
dir = end - start ;
VectorNormalize ( dir ) ;
}
void UTIL_AxisStringToUnitDir ( Vector & dir , const char * pString )
{
Vector start ;
UTIL_AxisStringToPointDir ( start , dir , pString ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
UTIL_ClipPunchAngleOffset
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
void UTIL_ClipPunchAngleOffset ( QAngle & in , const QAngle & punch , const QAngle & clip )
{
QAngle final = in + punch ;
//Clip each component
for ( int i = 0 ; i < 3 ; i + + )
{
if ( final [ i ] > clip [ i ] )
{
final [ i ] = clip [ i ] ;
}
else if ( final [ i ] < - clip [ i ] )
{
final [ i ] = - clip [ i ] ;
}
//Return the result
in [ i ] = final [ i ] - punch [ i ] ;
}
}
float UTIL_WaterLevel ( const Vector & position , float minz , float maxz )
{
Vector midUp = position ;
midUp . z = minz ;
if ( ! ( UTIL_PointContents ( midUp ) & MASK_WATER ) )
return minz ;
midUp . z = maxz ;
if ( UTIL_PointContents ( midUp ) & MASK_WATER )
return maxz ;
float diff = maxz - minz ;
while ( diff > 1.0 )
{
midUp . z = minz + diff / 2.0 ;
if ( UTIL_PointContents ( midUp ) & MASK_WATER )
{
minz = midUp . z ;
}
else
{
maxz = midUp . z ;
}
diff = maxz - minz ;
}
return midUp . z ;
}
//-----------------------------------------------------------------------------
// Like UTIL_WaterLevel, but *way* less expensive.
// I didn't replace UTIL_WaterLevel everywhere to avoid breaking anything.
//-----------------------------------------------------------------------------
class CWaterTraceFilter : public CTraceFilter
{
public :
bool ShouldHitEntity ( IHandleEntity * pHandleEntity , int contentsMask )
{
CBaseEntity * pCollide = EntityFromEntityHandle ( pHandleEntity ) ;
// Static prop case...
if ( ! pCollide )
return false ;
// Only impact water stuff...
if ( pCollide - > GetSolidFlags ( ) & FSOLID_VOLUME_CONTENTS )
return true ;
return false ;
}
} ;
float UTIL_FindWaterSurface ( const Vector & position , float minz , float maxz )
{
Vector vecStart , vecEnd ;
vecStart . Init ( position . x , position . y , maxz ) ;
vecEnd . Init ( position . x , position . y , minz ) ;
Ray_t ray ;
trace_t tr ;
CWaterTraceFilter waterTraceFilter ;
ray . Init ( vecStart , vecEnd ) ;
enginetrace - > TraceRay ( ray , MASK_WATER , & waterTraceFilter , & tr ) ;
return tr . endpos . z ;
}
extern short g_sModelIndexBubbles ; // holds the index for the bubbles model
void UTIL_Bubbles ( const Vector & mins , const Vector & maxs , int count )
{
Vector mid = ( mins + maxs ) * 0.5 ;
float flHeight = UTIL_WaterLevel ( mid , mid . z , mid . z + 1024 ) ;
flHeight = flHeight - mins . z ;
CPASFilter filter ( mid ) ;
te - > Bubbles ( filter , 0.0 ,
& mins , & maxs , flHeight , g_sModelIndexBubbles , count , 8.0 ) ;
}
void UTIL_BubbleTrail ( const Vector & from , const Vector & to , int count )
{
// Find water surface will return from.z if the from point is above water
float flStartHeight = UTIL_FindWaterSurface ( from , from . z , from . z + 256 ) ;
flStartHeight = flStartHeight - from . z ;
float flEndHeight = UTIL_FindWaterSurface ( to , to . z , to . z + 256 ) ;
flEndHeight = flEndHeight - to . z ;
if ( ( flStartHeight = = 0 ) & & ( flEndHeight = = 0 ) )
return ;
float flWaterZ = flStartHeight + from . z ;
const Vector * pFrom = & from ;
const Vector * pTo = & to ;
Vector vecWaterPoint ;
if ( ( flStartHeight = = 0 ) | | ( flEndHeight = = 0 ) )
{
if ( flStartHeight = = 0 )
{
flWaterZ = flEndHeight + to . z ;
}
float t = IntersectRayWithAAPlane ( from , to , 2 , 1.0f , flWaterZ ) ;
Assert ( ( t > = - 1e-3 f ) & & ( t < = 1.0f ) ) ;
VectorLerp ( from , to , t , vecWaterPoint ) ;
if ( flStartHeight = = 0 )
{
pFrom = & vecWaterPoint ;
// Reduce the count by the actual length
count = ( int ) ( count * ( 1.0f - t ) ) ;
}
else
{
pTo = & vecWaterPoint ;
// Reduce the count by the actual length
count = ( int ) ( count * t ) ;
}
}
CBroadcastRecipientFilter filter ;
te - > BubbleTrail ( filter , 0.0 , pFrom , pTo , flWaterZ , g_sModelIndexBubbles , count , 8.0 ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : Start -
// End -
// ModelIndex -
// FrameStart -
// FrameRate -
// Life -
// Width -
// Noise -
// Red -
// Green -
// Brightness -
// Speed -
//-----------------------------------------------------------------------------
void UTIL_Beam ( Vector & Start , Vector & End , int nModelIndex , int nHaloIndex , unsigned char FrameStart , unsigned char FrameRate ,
float Life , unsigned char Width , unsigned char EndWidth , unsigned char FadeLength , unsigned char Noise , unsigned char Red , unsigned char Green ,
unsigned char Blue , unsigned char Brightness , unsigned char Speed )
{
CBroadcastRecipientFilter filter ;
te - > BeamPoints ( filter , 0.0 ,
& Start ,
& End ,
nModelIndex ,
nHaloIndex ,
FrameStart ,
FrameRate ,
Life ,
Width ,
EndWidth ,
FadeLength ,
Noise ,
Red ,
Green ,
Blue ,
Brightness ,
Speed ) ;
}
bool UTIL_IsValidEntity ( CBaseEntity * pEnt )
{
edict_t * pEdict = pEnt - > edict ( ) ;
if ( ! pEdict | | pEdict - > IsFree ( ) )
return false ;
return true ;
}
# define PRECACHE_OTHER_ONCE
// UNDONE: Do we need this to avoid doing too much of this? Measure startup times and see
# if defined( PRECACHE_OTHER_ONCE )
# include "utlsymbol.h"
class CPrecacheOtherList : public CAutoGameSystem
{
public :
CPrecacheOtherList ( char const * name ) : CAutoGameSystem ( name )
{
}
virtual void LevelInitPreEntity ( ) ;
virtual void LevelShutdownPostEntity ( ) ;
bool AddOrMarkPrecached ( const char * pClassname ) ;
private :
CUtlSymbolTable m_list ;
} ;
void CPrecacheOtherList : : LevelInitPreEntity ( )
{
m_list . RemoveAll ( ) ;
}
void CPrecacheOtherList : : LevelShutdownPostEntity ( )
{
m_list . RemoveAll ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: mark or add
// Input : *pEntity -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CPrecacheOtherList : : AddOrMarkPrecached ( const char * pClassname )
{
CUtlSymbol sym = m_list . Find ( pClassname ) ;
if ( sym . IsValid ( ) )
return false ;
m_list . AddString ( pClassname ) ;
return true ;
}
CPrecacheOtherList g_PrecacheOtherList ( " CPrecacheOtherList " ) ;
# endif
//-----------------------------------------------------------------------------
// Purpose:
// Input : *szClassname -
// *modelName -
//-----------------------------------------------------------------------------
void UTIL_PrecacheOther ( const char * szClassname , const char * modelName )
{
# if defined( PRECACHE_OTHER_ONCE )
// already done this one?, if not, mark as done
if ( ! g_PrecacheOtherList . AddOrMarkPrecached ( szClassname ) )
return ;
# endif
CBaseEntity * pEntity = CreateEntityByName ( szClassname ) ;
if ( ! pEntity )
{
Warning ( " NULL Ent in UTIL_PrecacheOther \n " ) ;
return ;
}
// If we have a specified model, set it before calling precache
if ( modelName & & modelName [ 0 ] )
{
pEntity - > SetModelName ( AllocPooledString ( modelName ) ) ;
}
if ( pEntity )
pEntity - > Precache ( ) ;
UTIL_RemoveImmediate ( pEntity ) ;
}
//=========================================================
// UTIL_LogPrintf - Prints a logged message to console.
// Preceded by LOG: ( timestamp ) < message >
//=========================================================
void UTIL_LogPrintf ( const char * fmt , . . . )
{
va_list argptr ;
char tempString [ 1024 ] ;
va_start ( argptr , fmt ) ;
Q_vsnprintf ( tempString , sizeof ( tempString ) , fmt , argptr ) ;
va_end ( argptr ) ;
// Print to server console
engine - > LogPrint ( tempString ) ;
}
//=========================================================
// UTIL_DotPoints - returns the dot product of a line from
// src to check and vecdir.
//=========================================================
float UTIL_DotPoints ( const Vector & vecSrc , const Vector & vecCheck , const Vector & vecDir )
{
Vector2D vec2LOS ;
vec2LOS = ( vecCheck - vecSrc ) . AsVector2D ( ) ;
Vector2DNormalize ( vec2LOS ) ;
return DotProduct2D ( vec2LOS , vecDir . AsVector2D ( ) ) ;
}
//=========================================================
// UTIL_StripToken - for redundant keynames
//=========================================================
void UTIL_StripToken ( const char * pKey , char * pDest )
{
int i = 0 ;
while ( pKey [ i ] & & pKey [ i ] ! = ' # ' )
{
pDest [ i ] = pKey [ i ] ;
i + + ;
}
pDest [ i ] = 0 ;
}
// computes gravity scale for an absolute gravity. Pass the result into CBaseEntity::SetGravity()
float UTIL_ScaleForGravity ( float desiredGravity )
{
float worldGravity = GetCurrentGravity ( ) ;
return worldGravity > 0 ? desiredGravity / worldGravity : 0 ;
}
//-----------------------------------------------------------------------------
// Purpose: Implemented for mathlib.c error handling
// Input : *error -
//-----------------------------------------------------------------------------
extern " C " void Sys_Error ( char * error , . . . )
{
va_list argptr ;
char string [ 1024 ] ;
va_start ( argptr , error ) ;
Q_vsnprintf ( string , sizeof ( string ) , error , argptr ) ;
va_end ( argptr ) ;
Warning ( " %s " , string ) ;
Assert ( 0 ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Spawns an entity into the game, initializing it with the map ent data block
// Input : *pEntity - the newly created entity
// *mapData - pointer a block of entity map data
// Output : -1 if the entity was not successfully created; 0 on success
//-----------------------------------------------------------------------------
int DispatchSpawn ( CBaseEntity * pEntity )
{
if ( pEntity )
{
MDLCACHE_CRITICAL_SECTION ( ) ;
// keep a smart pointer that will now if the object gets deleted
EHANDLE pEntSafe ;
pEntSafe = pEntity ;
// Initialize these or entities who don't link to the world won't have anything in here
// is this necessary?
//pEntity->SetAbsMins( pEntity->GetOrigin() - Vector(1,1,1) );
//pEntity->SetAbsMaxs( pEntity->GetOrigin() + Vector(1,1,1) );
# if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG)
const char * pszClassname = NULL ;
int iClassname = ( ( CEntityFactoryDictionary * ) EntityFactoryDictionary ( ) ) - > m_Factories . Find ( pEntity - > GetClassname ( ) ) ;
if ( iClassname ! = ( ( CEntityFactoryDictionary * ) EntityFactoryDictionary ( ) ) - > m_Factories . InvalidIndex ( ) )
pszClassname = ( ( CEntityFactoryDictionary * ) EntityFactoryDictionary ( ) ) - > m_Factories . GetElementName ( iClassname ) ;
if ( pszClassname )
{
MemAlloc_PushAllocDbgInfo ( pszClassname , __LINE__ ) ;
}
# endif
bool bAsyncAnims = mdlcache - > SetAsyncLoad ( MDLCACHE_ANIMBLOCK , false ) ;
CBaseAnimating * pAnimating = pEntity - > GetBaseAnimating ( ) ;
if ( ! pAnimating )
{
pEntity - > Spawn ( ) ;
}
else
{
// Don't allow the PVS check to skip animation setup during spawning
pAnimating - > SetBoneCacheFlags ( BCF_IS_IN_SPAWN ) ;
pEntity - > Spawn ( ) ;
if ( pEntSafe ! = NULL )
pAnimating - > ClearBoneCacheFlags ( BCF_IS_IN_SPAWN ) ;
}
mdlcache - > SetAsyncLoad ( MDLCACHE_ANIMBLOCK , bAsyncAnims ) ;
# if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG)
if ( pszClassname )
{
MemAlloc_PopAllocDbgInfo ( ) ;
}
# endif
// Try to get the pointer again, in case the spawn function deleted the entity.
// UNDONE: Spawn() should really return a code to ask that the entity be deleted, but
// that would touch too much code for me to do that right now.
if ( pEntSafe = = NULL | | pEntity - > IsMarkedForDeletion ( ) )
return - 1 ;
if ( pEntity - > m_iGlobalname ! = NULL_STRING )
{
// Handle global stuff here
int globalIndex = GlobalEntity_GetIndex ( pEntity - > m_iGlobalname ) ;
if ( globalIndex > = 0 )
{
// Already dead? delete
if ( GlobalEntity_GetState ( globalIndex ) = = GLOBAL_DEAD )
{
pEntity - > Remove ( ) ;
return - 1 ;
}
else if ( ! FStrEq ( STRING ( gpGlobals - > mapname ) , GlobalEntity_GetMap ( globalIndex ) ) )
{
pEntity - > MakeDormant ( ) ; // Hasn't been moved to this level yet, wait but stay alive
}
// In this level & not dead, continue on as normal
}
else
{
// Spawned entities default to 'On'
GlobalEntity_Add ( pEntity - > m_iGlobalname , gpGlobals - > mapname , GLOBAL_ON ) ;
// Msg( "Added global entity %s (%s)\n", pEntity->GetClassname(), STRING(pEntity->m_iGlobalname) );
}
}
gEntList . NotifySpawn ( pEntity ) ;
}
return 0 ;
}
// UNDONE: This could be a better test - can we run the absbox through the bsp and see
// if it contains any solid space? or would that eliminate some entities we want to keep?
int UTIL_EntityInSolid ( CBaseEntity * ent )
{
Vector point ;
CBaseEntity * pParent = ent - > GetMoveParent ( ) ;
// HACKHACK -- If you're attached to a client, always go through
if ( pParent )
{
if ( pParent - > IsPlayer ( ) )
return 0 ;
ent = ent - > GetRootMoveParent ( ) ;
}
point = ent - > WorldSpaceCenter ( ) ;
return ( enginetrace - > GetPointContents ( point ) & MASK_SOLID ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Initialize the matrix from an entity
// Input : *pEntity -
//-----------------------------------------------------------------------------
void EntityMatrix : : InitFromEntity ( CBaseEntity * pEntity , int iAttachment )
{
if ( ! pEntity )
{
Identity ( ) ;
return ;
}
// Get an attachment's matrix?
if ( iAttachment ! = 0 )
{
CBaseAnimating * pAnimating = pEntity - > GetBaseAnimating ( ) ;
if ( pAnimating & & pAnimating - > GetModelPtr ( ) )
{
Vector vOrigin ;
QAngle vAngles ;
if ( pAnimating - > GetAttachment ( iAttachment , vOrigin , vAngles ) )
{
( ( VMatrix * ) this ) - > SetupMatrixOrgAngles ( vOrigin , vAngles ) ;
return ;
}
}
}
( ( VMatrix * ) this ) - > SetupMatrixOrgAngles ( pEntity - > GetAbsOrigin ( ) , pEntity - > GetAbsAngles ( ) ) ;
}
void EntityMatrix : : InitFromEntityLocal ( CBaseEntity * entity )
{
if ( ! entity | | ! entity - > edict ( ) )
{
Identity ( ) ;
return ;
}
( ( VMatrix * ) this ) - > SetupMatrixOrgAngles ( entity - > GetLocalOrigin ( ) , entity - > GetLocalAngles ( ) ) ;
}
//==================================================
// Purpose:
// Input:
// Output:
//==================================================
void UTIL_ValidateSoundName ( string_t & name , const char * defaultStr )
{
if ( ( ! name | |
strlen ( ( char * ) STRING ( name ) ) < 1 ) | |
! Q_stricmp ( ( char * ) STRING ( name ) , " 0 " ) )
{
name = AllocPooledString ( defaultStr ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Slightly modified strtok. Does not modify the input string. Does
// not skip over more than one separator at a time. This allows parsing
// strings where tokens between separators may or may not be present:
//
// Door01,,,0 would be parsed as "Door01" "" "" "0"
// Door01,Open,,0 would be parsed as "Door01" "Open" "" "0"
//
// Input : token - Returns with a token, or zero length if the token was missing.
// str - String to parse.
// sep - Character to use as separator. UNDONE: allow multiple separator chars
// Output : Returns a pointer to the next token to be parsed.
//-----------------------------------------------------------------------------
const char * nexttoken ( char * token , const char * str , char sep )
{
if ( ( str = = NULL ) | | ( * str = = ' \0 ' ) )
{
* token = ' \0 ' ;
return ( NULL ) ;
}
//
// Copy everything up to the first separator into the return buffer.
// Do not include separators in the return buffer.
//
while ( ( * str ! = sep ) & & ( * str ! = ' \0 ' ) )
{
* token + + = * str + + ;
}
* token = ' \0 ' ;
//
// Advance the pointer unless we hit the end of the input string.
//
if ( * str = = ' \0 ' )
{
return ( str ) ;
}
return ( + + str ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Helper for UTIL_FindClientInPVS
// Input : check - last checked client
// Output : static int UTIL_GetNewCheckClient
//-----------------------------------------------------------------------------
// FIXME: include bspfile.h here?
class CCheckClient : public CAutoGameSystem
{
public :
CCheckClient ( char const * name ) : CAutoGameSystem ( name )
{
}
void LevelInitPreEntity ( )
{
m_checkCluster = - 1 ;
m_lastcheck = 1 ;
m_lastchecktime = - 1 ;
m_bClientPVSIsExpanded = false ;
}
byte m_checkPVS [ MAX_MAP_LEAFS / 8 ] ;
byte m_checkVisibilityPVS [ MAX_MAP_LEAFS / 8 ] ;
int m_checkCluster ;
int m_lastcheck ;
float m_lastchecktime ;
bool m_bClientPVSIsExpanded ;
} ;
CCheckClient g_CheckClient ( " CCheckClient " ) ;
static int UTIL_GetNewCheckClient ( int check )
{
int i ;
edict_t * ent ;
Vector org ;
// cycle to the next one
if ( check < 1 )
check = 1 ;
if ( check > gpGlobals - > maxClients )
check = gpGlobals - > maxClients ;
if ( check = = gpGlobals - > maxClients )
i = 1 ;
else
i = check + 1 ;
for ( ; ; i + + )
{
if ( i > gpGlobals - > maxClients )
{
i = 1 ;
}
ent = engine - > PEntityOfEntIndex ( i ) ;
if ( ! ent )
continue ;
// Looped but didn't find anything else
if ( i = = check )
break ;
if ( ! ent - > GetUnknown ( ) )
continue ;
CBaseEntity * entity = GetContainingEntity ( ent ) ;
if ( ! entity )
continue ;
if ( entity - > GetFlags ( ) & FL_NOTARGET )
continue ;
// anything that is a client, or has a client as an enemy
break ;
}
if ( i ! = check )
{
memset ( g_CheckClient . m_checkVisibilityPVS , 0 , sizeof ( g_CheckClient . m_checkVisibilityPVS ) ) ;
g_CheckClient . m_bClientPVSIsExpanded = false ;
}
if ( ent )
{
// get the PVS for the entity
CBaseEntity * pce = GetContainingEntity ( ent ) ;
if ( ! pce )
return i ;
org = pce - > EyePosition ( ) ;
int clusterIndex = engine - > GetClusterForOrigin ( org ) ;
if ( clusterIndex ! = g_CheckClient . m_checkCluster )
{
g_CheckClient . m_checkCluster = clusterIndex ;
engine - > GetPVSForCluster ( clusterIndex , sizeof ( g_CheckClient . m_checkPVS ) , g_CheckClient . m_checkPVS ) ;
}
}
return i ;
}
//-----------------------------------------------------------------------------
// Gets the current check client....
//-----------------------------------------------------------------------------
static edict_t * UTIL_GetCurrentCheckClient ( )
{
edict_t * ent ;
// find a new check if on a new frame
float delta = gpGlobals - > curtime - g_CheckClient . m_lastchecktime ;
if ( delta > = 0.1 | | delta < 0 )
{
g_CheckClient . m_lastcheck = UTIL_GetNewCheckClient ( g_CheckClient . m_lastcheck ) ;
g_CheckClient . m_lastchecktime = gpGlobals - > curtime ;
}
// return check if it might be visible
ent = engine - > PEntityOfEntIndex ( g_CheckClient . m_lastcheck ) ;
// Allow dead clients -- JAY
// Our monsters know the difference, and this function gates alot of behavior
// It's annoying to die and see monsters stop thinking because you're no longer
// "in" their PVS
if ( ! ent | | ent - > IsFree ( ) | | ! ent - > GetUnknown ( ) )
{
return NULL ;
}
return ent ;
}
void UTIL_SetClientVisibilityPVS ( edict_t * pClient , const unsigned char * pvs , int pvssize )
{
if ( pClient = = UTIL_GetCurrentCheckClient ( ) )
{
Assert ( pvssize < = sizeof ( g_CheckClient . m_checkVisibilityPVS ) ) ;
g_CheckClient . m_bClientPVSIsExpanded = false ;
unsigned * pFrom = ( unsigned * ) pvs ;
unsigned * pMask = ( unsigned * ) g_CheckClient . m_checkPVS ;
unsigned * pTo = ( unsigned * ) g_CheckClient . m_checkVisibilityPVS ;
int limit = pvssize / 4 ;
int i ;
for ( i = 0 ; i < limit ; i + + )
{
pTo [ i ] = pFrom [ i ] & ~ pMask [ i ] ;
if ( pFrom [ i ] )
{
g_CheckClient . m_bClientPVSIsExpanded = true ;
}
}
int remainder = pvssize % 4 ;
for ( i = 0 ; i < remainder ; i + + )
{
( ( unsigned char * ) & pTo [ limit ] ) [ i ] = ( ( unsigned char * ) & pFrom [ limit ] ) [ i ] & ! ( ( unsigned char * ) & pMask [ limit ] ) [ i ] ;
if ( ( ( unsigned char * ) & pFrom [ limit ] ) [ i ] ! = 0 )
{
g_CheckClient . m_bClientPVSIsExpanded = true ;
}
}
}
}
bool UTIL_ClientPVSIsExpanded ( )
{
return g_CheckClient . m_bClientPVSIsExpanded ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns a client (or object that has a client enemy) that would be a valid target.
// If there are more than one valid options, they are cycled each frame
// If (self.origin + self.viewofs) is not in the PVS of the current target, it is not returned at all.
// Input : *pEdict -
// Output : edict_t*
//-----------------------------------------------------------------------------
CBaseEntity * UTIL_FindClientInPVS ( const Vector & vecBoxMins , const Vector & vecBoxMaxs )
{
edict_t * ent = UTIL_GetCurrentCheckClient ( ) ;
if ( ! ent )
{
return NULL ;
}
if ( ! engine - > CheckBoxInPVS ( vecBoxMins , vecBoxMaxs , g_CheckClient . m_checkPVS , sizeof ( g_CheckClient . m_checkPVS ) ) )
{
return NULL ;
}
// might be able to see it
return GetContainingEntity ( ent ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns a client (or object that has a client enemy) that would be a valid target.
// If there are more than one valid options, they are cycled each frame
// If (self.origin + self.viewofs) is not in the PVS of the current target, it is not returned at all.
// Input : *pEdict -
// Output : edict_t*
//-----------------------------------------------------------------------------
ConVar sv_strict_notarget ( " sv_strict_notarget " , " 0 " , 0 , " If set, notarget will cause entities to never think they are in the pvs " ) ;
static edict_t * UTIL_FindClientInPVSGuts ( edict_t * pEdict , unsigned char * pvs , unsigned pvssize )
{
Vector view ;
edict_t * ent = UTIL_GetCurrentCheckClient ( ) ;
if ( ! ent )
{
return NULL ;
}
CBaseEntity * pPlayerEntity = GetContainingEntity ( ent ) ;
if ( ( ! pPlayerEntity | | ( pPlayerEntity - > GetFlags ( ) & FL_NOTARGET ) ) & & sv_strict_notarget . GetBool ( ) )
{
return NULL ;
}
// if current entity can't possibly see the check entity, return 0
// UNDONE: Build a box for this and do it over that box
// UNDONE: Use CM_BoxLeafnums()
CBaseEntity * pe = GetContainingEntity ( pEdict ) ;
if ( pe )
{
view = pe - > EyePosition ( ) ;
if ( ! engine - > CheckOriginInPVS ( view , pvs , pvssize ) )
{
return NULL ;
}
}
// might be able to see it
return ent ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns a client that could see the entity directly
//-----------------------------------------------------------------------------
edict_t * UTIL_FindClientInPVS ( edict_t * pEdict )
{
return UTIL_FindClientInPVSGuts ( pEdict , g_CheckClient . m_checkPVS , sizeof ( g_CheckClient . m_checkPVS ) ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns a client that could see the entity, including through a camera
//-----------------------------------------------------------------------------
edict_t * UTIL_FindClientInVisibilityPVS ( edict_t * pEdict )
{
return UTIL_FindClientInPVSGuts ( pEdict , g_CheckClient . m_checkVisibilityPVS , sizeof ( g_CheckClient . m_checkVisibilityPVS ) ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns a chain of entities within the PVS of another entity (client)
// starting_ent is the ent currently at in the list
// a starting_ent of NULL signifies the beginning of a search
// Input : *pplayer -
// *starting_ent -
// Output : edict_t
//-----------------------------------------------------------------------------
CBaseEntity * UTIL_EntitiesInPVS ( CBaseEntity * pPVSEntity , CBaseEntity * pStartingEntity )
{
Vector org ;
static byte pvs [ MAX_MAP_CLUSTERS / 8 ] ;
static Vector lastOrg ( 0 , 0 , 0 ) ;
static int lastCluster = - 1 ;
if ( ! pPVSEntity )
return NULL ;
// NOTE: These used to be caching code here to prevent this from
// being called over+over which breaks when you go back + forth
// across level transitions
// So, we'll always get the PVS each time we start a new EntitiesInPVS iteration.
// Given that weapon_binocs + leveltransition code is the only current clients
// of this, this seems safe.
if ( ! pStartingEntity )
{
org = pPVSEntity - > EyePosition ( ) ;
int clusterIndex = engine - > GetClusterForOrigin ( org ) ;
Assert ( clusterIndex > = 0 ) ;
engine - > GetPVSForCluster ( clusterIndex , sizeof ( pvs ) , pvs ) ;
}
for ( CBaseEntity * pEntity = gEntList . NextEnt ( pStartingEntity ) ; pEntity ; pEntity = gEntList . NextEnt ( pEntity ) )
{
// Only return attached ents.
if ( ! pEntity - > edict ( ) )
continue ;
CBaseEntity * pParent = pEntity - > GetRootMoveParent ( ) ;
Vector vecSurroundMins , vecSurroundMaxs ;
pParent - > CollisionProp ( ) - > WorldSpaceSurroundingBounds ( & vecSurroundMins , & vecSurroundMaxs ) ;
if ( ! engine - > CheckBoxInPVS ( vecSurroundMins , vecSurroundMaxs , pvs , sizeof ( pvs ) ) )
continue ;
return pEntity ;
}
return NULL ;
}
//-----------------------------------------------------------------------------
// Purpose: Get the predicted postion of an entity of a certain number of seconds
// Use this function with caution, it has great potential for annoying the player, especially
// if used for target firing predition
// Input : *pTarget - target entity to predict
// timeDelta - amount of time to predict ahead (in seconds)
// &vecPredictedPosition - output
//-----------------------------------------------------------------------------
void UTIL_PredictedPosition ( CBaseEntity * pTarget , float flTimeDelta , Vector * vecPredictedPosition )
{
if ( ( pTarget = = NULL ) | | ( vecPredictedPosition = = NULL ) )
return ;
Vector vecPredictedVel ;
//FIXME: Should we look at groundspeed or velocity for non-clients??
//Get the proper velocity to predict with
CBasePlayer * pPlayer = ToBasePlayer ( pTarget ) ;
//Player works differently than other entities
if ( pPlayer ! = NULL )
{
if ( pPlayer - > IsInAVehicle ( ) )
{
//Calculate the predicted position in this vehicle
vecPredictedVel = pPlayer - > GetVehicleEntity ( ) - > GetSmoothedVelocity ( ) ;
}
else
{
//Get the player's stored velocity
vecPredictedVel = pPlayer - > GetSmoothedVelocity ( ) ;
}
}
else
{
// See if we're a combat character in a vehicle
CBaseCombatCharacter * pCCTarget = pTarget - > MyCombatCharacterPointer ( ) ;
if ( pCCTarget ! = NULL & & pCCTarget - > IsInAVehicle ( ) )
{
//Calculate the predicted position in this vehicle
vecPredictedVel = pCCTarget - > GetVehicleEntity ( ) - > GetSmoothedVelocity ( ) ;
}
else
{
// See if we're an animating entity
CBaseAnimating * pAnimating = dynamic_cast < CBaseAnimating * > ( pTarget ) ;
if ( pAnimating ! = NULL )
{
vecPredictedVel = pAnimating - > GetGroundSpeedVelocity ( ) ;
}
else
{
// Otherwise we're a vanilla entity
vecPredictedVel = pTarget - > GetSmoothedVelocity ( ) ;
}
}
}
//Get the result
( * vecPredictedPosition ) = pTarget - > GetAbsOrigin ( ) + ( vecPredictedVel * flTimeDelta ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Points the destination entity at the target entity
// Input : *pDest - entity to be pointed at the target
// *pTarget - target to point at
//-----------------------------------------------------------------------------
bool UTIL_PointAtEntity ( CBaseEntity * pDest , CBaseEntity * pTarget )
{
if ( ( pDest = = NULL ) | | ( pTarget = = NULL ) )
{
return false ;
}
Vector dir = ( pTarget - > GetAbsOrigin ( ) - pDest - > GetAbsOrigin ( ) ) ;
VectorNormalize ( dir ) ;
//Store off as angles
QAngle angles ;
VectorAngles ( dir , angles ) ;
pDest - > SetLocalAngles ( angles ) ;
pDest - > SetAbsAngles ( angles ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Points the destination entity at the target entity by name
// Input : *pDest - entity to be pointed at the target
// strTarget - name of entity to target (will only choose the first!)
//-----------------------------------------------------------------------------
void UTIL_PointAtNamedEntity ( CBaseEntity * pDest , string_t strTarget )
{
//Attempt to find the entity
if ( ! UTIL_PointAtEntity ( pDest , gEntList . FindEntityByName ( NULL , strTarget ) ) )
{
DevMsg ( 1 , " %s (%s) was unable to point at an entity named: %s \n " , pDest - > GetClassname ( ) , pDest - > GetDebugName ( ) , STRING ( strTarget ) ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Copy the pose parameter values from one entity to the other
// Input : *pSourceEntity - entity to copy from
// *pDestEntity - entity to copy to
//-----------------------------------------------------------------------------
bool UTIL_TransferPoseParameters ( CBaseEntity * pSourceEntity , CBaseEntity * pDestEntity )
{
CBaseAnimating * pSourceBaseAnimating = dynamic_cast < CBaseAnimating * > ( pSourceEntity ) ;
CBaseAnimating * pDestBaseAnimating = dynamic_cast < CBaseAnimating * > ( pDestEntity ) ;
if ( ! pSourceBaseAnimating | | ! pDestBaseAnimating )
return false ;
for ( int iPose = 0 ; iPose < MAXSTUDIOPOSEPARAM ; + + iPose )
{
pDestBaseAnimating - > SetPoseParameter ( iPose , pSourceBaseAnimating - > GetPoseParameter ( iPose ) ) ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Make a muzzle flash appear
// Input : &origin - position of the muzzle flash
// &angles - angles of the fire direction
// scale - scale of the muzzle flash
// type - type of muzzle flash
//-----------------------------------------------------------------------------
void UTIL_MuzzleFlash ( const Vector & origin , const QAngle & angles , int scale , int type )
{
CPASFilter filter ( origin ) ;
te - > MuzzleFlash ( filter , 0.0f , origin , angles , scale , type ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : vStartPos - start of the line
// vEndPos - end of the line
// vPoint - point to find nearest point to on specified line
// clampEnds - clamps returned points to being on the line segment specified
// Output : Vector - nearest point on the specified line
//-----------------------------------------------------------------------------
Vector UTIL_PointOnLineNearestPoint ( const Vector & vStartPos , const Vector & vEndPos , const Vector & vPoint , bool clampEnds )
{
Vector vEndToStart = ( vEndPos - vStartPos ) ;
Vector vOrgToStart = ( vPoint - vStartPos ) ;
float fNumerator = DotProduct ( vEndToStart , vOrgToStart ) ;
float fDenominator = vEndToStart . Length ( ) * vOrgToStart . Length ( ) ;
float fIntersectDist = vOrgToStart . Length ( ) * ( fNumerator / fDenominator ) ;
float flLineLength = VectorNormalize ( vEndToStart ) ;
if ( clampEnds )
{
fIntersectDist = clamp ( fIntersectDist , 0.0f , flLineLength ) ;
}
Vector vIntersectPos = vStartPos + vEndToStart * fIntersectDist ;
return vIntersectPos ;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
AngularImpulse WorldToLocalRotation ( const VMatrix & localToWorld , const Vector & worldAxis , float rotation )
{
// fix axes of rotation to match axes of vector
Vector rot = worldAxis * rotation ;
// since the matrix maps local to world, do a transpose rotation to get world to local
AngularImpulse ang = localToWorld . VMul3x3Transpose ( rot ) ;
return ang ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *filename -
// *pLength -
// Output : byte
//-----------------------------------------------------------------------------
byte * UTIL_LoadFileForMe ( const char * filename , int * pLength )
{
void * buffer = NULL ;
int length = filesystem - > ReadFileEx ( filename , " GAME " , & buffer , true , true ) ;
if ( pLength )
{
* pLength = length ;
}
return ( byte * ) buffer ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *buffer -
//-----------------------------------------------------------------------------
void UTIL_FreeFile ( byte * buffer )
{
filesystem - > FreeOptimalReadBuffer ( buffer ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Determines whether an entity is within a certain angular tolerance to viewer
// Input : *pEntity - entity which is the "viewer"
// vecPosition - position to test against
// flTolerance - tolerance (as dot-product)
// *pflDot - if not NULL, holds the
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool UTIL_IsFacingWithinTolerance ( CBaseEntity * pViewer , const Vector & vecPosition , float flDotTolerance , float * pflDot /*= NULL*/ )
{
if ( pflDot )
{
* pflDot = 0.0f ;
}
// Required elements
if ( pViewer = = NULL )
return false ;
Vector forward ;
pViewer - > GetVectors ( & forward , NULL , NULL ) ;
Vector dir = vecPosition - pViewer - > GetAbsOrigin ( ) ;
VectorNormalize ( dir ) ;
// Larger dot product corresponds to a smaller angle
float flDot = dir . Dot ( forward ) ;
// Return the result
if ( pflDot )
{
* pflDot = flDot ;
}
// Within the goal tolerance
if ( flDot > = flDotTolerance )
return true ;
return false ;
}
//-----------------------------------------------------------------------------
// Purpose: Determines whether an entity is within a certain angular tolerance to viewer
// Input : *pEntity - entity which is the "viewer"
// *pTarget - entity to test against
// flTolerance - tolerance (as dot-product)
// *pflDot - if not NULL, holds the
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool UTIL_IsFacingWithinTolerance ( CBaseEntity * pViewer , CBaseEntity * pTarget , float flDotTolerance , float * pflDot /*= NULL*/ )
{
if ( pViewer = = NULL | | pTarget = = NULL )
return false ;
return UTIL_IsFacingWithinTolerance ( pViewer , pTarget - > GetAbsOrigin ( ) , flDotTolerance , pflDot ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Fills in color for debug purposes based on a relationship
// Input : nRelationship - relationship to test
// *pR, *pG, *pB - colors to fill
//-----------------------------------------------------------------------------
void UTIL_GetDebugColorForRelationship ( int nRelationship , int & r , int & g , int & b )
{
switch ( nRelationship )
{
case D_LI :
r = 0 ;
g = 255 ;
b = 0 ;
break ;
case D_NU :
r = 0 ;
g = 0 ;
b = 255 ;
break ;
case D_HT :
r = 255 ;
g = 0 ;
b = 0 ;
break ;
case D_FR :
r = 255 ;
g = 255 ;
b = 0 ;
break ;
default :
r = 255 ;
g = 255 ;
b = 255 ;
break ;
}
}
void LoadAndSpawnEntities_ParseEntKVBlockHelper ( CBaseEntity * pNode , KeyValues * pkvNode )
{
KeyValues * pkvNodeData = pkvNode - > GetFirstSubKey ( ) ;
while ( pkvNodeData )
{
// Handle the connections block
if ( ! Q_strcmp ( pkvNodeData - > GetName ( ) , " connections " ) )
{
LoadAndSpawnEntities_ParseEntKVBlockHelper ( pNode , pkvNodeData ) ;
}
else
{
pNode - > KeyValue ( pkvNodeData - > GetName ( ) , pkvNodeData - > GetString ( ) ) ;
}
pkvNodeData = pkvNodeData - > GetNextKey ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Loads and parses a file and spawns entities defined in it.
//-----------------------------------------------------------------------------
bool UTIL_LoadAndSpawnEntitiesFromScript ( CUtlVector < CBaseEntity * > & entities , const char * pScriptFile , const char * pBlock , bool bActivate )
{
KeyValues * pkvFile = new KeyValues ( pBlock ) ;
if ( pkvFile - > LoadFromFile ( filesystem , pScriptFile , " MOD " ) )
{
// Load each block, and spawn the entities
KeyValues * pkvNode = pkvFile - > GetFirstSubKey ( ) ;
while ( pkvNode )
{
// Get name
const char * pNodeName = pkvNode - > GetName ( ) ;
if ( stricmp ( pNodeName , " entity " ) )
{
pkvNode = pkvNode - > GetNextKey ( ) ;
continue ;
}
KeyValues * pClassname = pkvNode - > FindKey ( " classname " ) ;
if ( pClassname )
{
// Use the classname instead
pNodeName = pClassname - > GetString ( ) ;
}
// Spawn the entity
CBaseEntity * pNode = CreateEntityByName ( pNodeName ) ;
if ( pNode )
{
LoadAndSpawnEntities_ParseEntKVBlockHelper ( pNode , pkvNode ) ;
DispatchSpawn ( pNode ) ;
entities . AddToTail ( pNode ) ;
}
else
{
Warning ( " UTIL_LoadAndSpawnEntitiesFromScript: Failed to spawn entity, type: '%s' \n " , pNodeName ) ;
}
// Move to next entity
pkvNode = pkvNode - > GetNextKey ( ) ;
}
if ( bActivate = = true )
{
bool bAsyncAnims = mdlcache - > SetAsyncLoad ( MDLCACHE_ANIMBLOCK , false ) ;
// Then activate all the entities
for ( int i = 0 ; i < entities . Count ( ) ; i + + )
{
entities [ i ] - > Activate ( ) ;
}
mdlcache - > SetAsyncLoad ( MDLCACHE_ANIMBLOCK , bAsyncAnims ) ;
}
}
else
return false ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Convert a vector an angle from worldspace to the entity's parent's local space
// Input : *pEntity - Entity whose parent we're concerned with
//-----------------------------------------------------------------------------
void UTIL_ParentToWorldSpace ( CBaseEntity * pEntity , Vector & vecPosition , QAngle & vecAngles )
{
if ( pEntity = = NULL )
return ;
// Construct the entity-to-world matrix
// Start with making an entity-to-parent matrix
matrix3x4_t matEntityToParent ;
AngleMatrix ( vecAngles , matEntityToParent ) ;
MatrixSetColumn ( vecPosition , 3 , matEntityToParent ) ;
// concatenate with our parent's transform
matrix3x4_t matScratch , matResult ;
matrix3x4_t matParentToWorld ;
if ( pEntity - > GetParent ( ) ! = NULL )
{
matParentToWorld = pEntity - > GetParentToWorldTransform ( matScratch ) ;
}
else
{
matParentToWorld = pEntity - > EntityToWorldTransform ( ) ;
}
ConcatTransforms ( matParentToWorld , matEntityToParent , matResult ) ;
// pull our absolute position out of the matrix
MatrixGetColumn ( matResult , 3 , vecPosition ) ;
MatrixAngles ( matResult , vecAngles ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Convert a vector and quaternion from worldspace to the entity's parent's local space
// Input : *pEntity - Entity whose parent we're concerned with
//-----------------------------------------------------------------------------
void UTIL_ParentToWorldSpace ( CBaseEntity * pEntity , Vector & vecPosition , Quaternion & quat )
{
if ( pEntity = = NULL )
return ;
QAngle vecAngles ;
QuaternionAngles ( quat , vecAngles ) ;
UTIL_ParentToWorldSpace ( pEntity , vecPosition , vecAngles ) ;
AngleQuaternion ( vecAngles , quat ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Convert a vector an angle from worldspace to the entity's parent's local space
// Input : *pEntity - Entity whose parent we're concerned with
//-----------------------------------------------------------------------------
void UTIL_WorldToParentSpace ( CBaseEntity * pEntity , Vector & vecPosition , QAngle & vecAngles )
{
if ( pEntity = = NULL )
return ;
// Construct the entity-to-world matrix
// Start with making an entity-to-parent matrix
matrix3x4_t matEntityToParent ;
AngleMatrix ( vecAngles , matEntityToParent ) ;
MatrixSetColumn ( vecPosition , 3 , matEntityToParent ) ;
// concatenate with our parent's transform
matrix3x4_t matScratch , matResult ;
matrix3x4_t matWorldToParent ;
if ( pEntity - > GetParent ( ) ! = NULL )
{
matScratch = pEntity - > GetParentToWorldTransform ( matScratch ) ;
}
else
{
matScratch = pEntity - > EntityToWorldTransform ( ) ;
}
MatrixInvert ( matScratch , matWorldToParent ) ;
ConcatTransforms ( matWorldToParent , matEntityToParent , matResult ) ;
// pull our absolute position out of the matrix
MatrixGetColumn ( matResult , 3 , vecPosition ) ;
MatrixAngles ( matResult , vecAngles ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Convert a vector and quaternion from worldspace to the entity's parent's local space
// Input : *pEntity - Entity whose parent we're concerned with
//-----------------------------------------------------------------------------
void UTIL_WorldToParentSpace ( CBaseEntity * pEntity , Vector & vecPosition , Quaternion & quat )
{
if ( pEntity = = NULL )
return ;
QAngle vecAngles ;
QuaternionAngles ( quat , vecAngles ) ;
UTIL_WorldToParentSpace ( pEntity , vecPosition , vecAngles ) ;
AngleQuaternion ( vecAngles , quat ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Given a vector, clamps the scalar axes to MAX_COORD_FLOAT ranges from worldsize.h
// Input : *pVecPos -
//-----------------------------------------------------------------------------
void UTIL_BoundToWorldSize ( Vector * pVecPos )
{
Assert ( pVecPos ) ;
for ( int i = 0 ; i < 3 ; + + i )
{
( * pVecPos ) [ i ] = clamp ( ( * pVecPos ) [ i ] , MIN_COORD_FLOAT , MAX_COORD_FLOAT ) ;
}
}
//=============================================================================
//
// Tests!
//
# define NUM_KDTREE_TESTS 2500
# define NUM_KDTREE_ENTITY_SIZE 256
void CC_KDTreeTest ( const CCommand & args )
{
Msg ( " Testing kd-tree entity queries. " ) ;
// Get the testing spot.
// CBaseEntity *pSpot = gEntList.FindEntityByClassname( NULL, "info_player_start" );
// Vector vecStart = pSpot->GetAbsOrigin();
CBasePlayer * pPlayer = static_cast < CBasePlayer * > ( UTIL_GetLocalPlayer ( ) ) ;
Vector vecStart = pPlayer - > GetAbsOrigin ( ) ;
static Vector * vecTargets = NULL ;
static bool bFirst = true ;
// Generate the targets - rays (1K long).
if ( bFirst )
{
vecTargets = new Vector [ NUM_KDTREE_TESTS ] ;
double flRadius = 0 ;
double flTheta = 0 ;
double flPhi = 0 ;
for ( int i = 0 ; i < NUM_KDTREE_TESTS ; + + i )
{
flRadius + = NUM_KDTREE_TESTS * 123.123 ;
flRadius = fmod ( flRadius , 128.0 ) ;
flRadius = fabs ( flRadius ) ;
flTheta + = NUM_KDTREE_TESTS * 76.76 ;
flTheta = fmod ( flTheta , ( double ) DEG2RAD ( 360 ) ) ;
flTheta = fabs ( flTheta ) ;
flPhi + = NUM_KDTREE_TESTS * 1997.99 ;
flPhi = fmod ( flPhi , ( double ) DEG2RAD ( 180 ) ) ;
flPhi = fabs ( flPhi ) ;
float st , ct , sp , cp ;
SinCos ( flTheta , & st , & ct ) ;
SinCos ( flPhi , & sp , & cp ) ;
vecTargets [ i ] . x = flRadius * ct * sp ;
vecTargets [ i ] . y = flRadius * st * sp ;
vecTargets [ i ] . z = flRadius * cp ;
// Make the trace 1024 units long.
Vector vecDir = vecTargets [ i ] - vecStart ;
VectorNormalize ( vecDir ) ;
vecTargets [ i ] = vecStart + vecDir * 1024 ;
}
bFirst = false ;
}
int nTestType = 0 ;
if ( args . ArgC ( ) > = 2 )
{
nTestType = atoi ( args [ 1 ] ) ;
}
vtune ( true ) ;
# ifdef VPROF_ENABLED
g_VProfCurrentProfile . Resume ( ) ;
g_VProfCurrentProfile . Start ( ) ;
g_VProfCurrentProfile . Reset ( ) ;
g_VProfCurrentProfile . MarkFrame ( ) ;
# endif
switch ( nTestType )
{
case 0 :
{
VPROF ( " TraceTotal " ) ;
trace_t trace ;
for ( int iTest = 0 ; iTest < NUM_KDTREE_TESTS ; + + iTest )
{
UTIL_TraceLine ( vecStart , vecTargets [ iTest ] , MASK_SOLID_BRUSHONLY , NULL , COLLISION_GROUP_NONE , & trace ) ;
}
break ;
}
case 1 :
{
VPROF ( " TraceTotal " ) ;
trace_t trace ;
for ( int iTest = 0 ; iTest < NUM_KDTREE_TESTS ; + + iTest )
{
UTIL_TraceHull ( vecStart , vecTargets [ iTest ] , VEC_HULL_MIN_SCALED ( pPlayer ) , VEC_HULL_MAX_SCALED ( pPlayer ) , MASK_SOLID , pPlayer , COLLISION_GROUP_NONE , & trace ) ;
}
break ;
}
case 2 :
{
Vector vecMins [ NUM_KDTREE_TESTS ] ;
Vector vecMaxs [ NUM_KDTREE_TESTS ] ;
int iTest ;
for ( iTest = 0 ; iTest < NUM_KDTREE_TESTS ; + + iTest )
{
vecMins [ iTest ] = vecStart ;
vecMaxs [ iTest ] = vecStart ;
for ( int iAxis = 0 ; iAxis < 3 ; + + iAxis )
{
if ( vecTargets [ iTest ] . x < vecMins [ iTest ] . x ) { vecMins [ iTest ] . x = vecTargets [ iTest ] . x ; }
if ( vecTargets [ iTest ] . y < vecMins [ iTest ] . y ) { vecMins [ iTest ] . y = vecTargets [ iTest ] . y ; }
if ( vecTargets [ iTest ] . z < vecMins [ iTest ] . z ) { vecMins [ iTest ] . z = vecTargets [ iTest ] . z ; }
if ( vecTargets [ iTest ] . x > vecMaxs [ iTest ] . x ) { vecMaxs [ iTest ] . x = vecTargets [ iTest ] . x ; }
if ( vecTargets [ iTest ] . y > vecMaxs [ iTest ] . y ) { vecMaxs [ iTest ] . y = vecTargets [ iTest ] . y ; }
if ( vecTargets [ iTest ] . z > vecMaxs [ iTest ] . z ) { vecMaxs [ iTest ] . z = vecTargets [ iTest ] . z ; }
}
}
VPROF ( " TraceTotal " ) ;
int nCount = 0 ;
Vector vecDelta ;
trace_t trace ;
CBaseEntity * pList [ 1024 ] ;
for ( iTest = 0 ; iTest < NUM_KDTREE_TESTS ; + + iTest )
{
nCount + = UTIL_EntitiesInBox ( pList , 1024 , vecMins [ iTest ] , vecMaxs [ iTest ] , 0 ) ;
}
Msg ( " Count = %d \n " , nCount ) ;
break ;
}
case 3 :
{
Vector vecDelta ;
float flRadius [ NUM_KDTREE_TESTS ] ;
int iTest ;
for ( iTest = 0 ; iTest < NUM_KDTREE_TESTS ; + + iTest )
{
VectorSubtract ( vecTargets [ iTest ] , vecStart , vecDelta ) ;
flRadius [ iTest ] = vecDelta . Length ( ) * 0.5f ;
}
VPROF ( " TraceTotal " ) ;
int nCount = 0 ;
trace_t trace ;
CBaseEntity * pList [ 1024 ] ;
for ( iTest = 0 ; iTest < NUM_KDTREE_TESTS ; + + iTest )
{
nCount + = UTIL_EntitiesInSphere ( pList , 1024 , vecStart , flRadius [ iTest ] , 0 ) ;
}
Msg ( " Count = %d \n " , nCount ) ;
break ;
}
default :
{
break ;
}
}
# ifdef VPROF_ENABLED
g_VProfCurrentProfile . MarkFrame ( ) ;
g_VProfCurrentProfile . Pause ( ) ;
g_VProfCurrentProfile . OutputReport ( VPRT_FULL ) ;
# endif
vtune ( false ) ;
}
static ConCommand kdtree_test ( " kdtree_test " , CC_KDTreeTest , " Tests spatial partition for entities queries. " , FCVAR_CHEAT ) ;
void CC_VoxelTreeView ( void )
{
Msg ( " VoxelTreeView \n " ) ;
partition - > RenderAllObjectsInTree ( 10.0f ) ;
}
static ConCommand voxeltree_view ( " voxeltree_view " , CC_VoxelTreeView , " View entities in the voxel-tree. " , FCVAR_CHEAT ) ;
void CC_VoxelTreePlayerView ( void )
{
Msg ( " VoxelTreePlayerView \n " ) ;
CBasePlayer * pPlayer = static_cast < CBasePlayer * > ( UTIL_GetLocalPlayer ( ) ) ;
Vector vecStart = pPlayer - > GetAbsOrigin ( ) ;
partition - > RenderObjectsInPlayerLeafs ( vecStart - VEC_HULL_MIN_SCALED ( pPlayer ) , vecStart + VEC_HULL_MAX_SCALED ( pPlayer ) , 3.0f ) ;
}
static ConCommand voxeltree_playerview ( " voxeltree_playerview " , CC_VoxelTreePlayerView , " View entities in the voxel-tree at the player position. " , FCVAR_CHEAT ) ;
void CC_VoxelTreeBox ( const CCommand & args )
{
Vector vecMin , vecMax ;
if ( args . ArgC ( ) > = 6 )
{
vecMin . x = atof ( args [ 1 ] ) ;
vecMin . y = atof ( args [ 2 ] ) ;
vecMin . z = atof ( args [ 3 ] ) ;
vecMax . x = atof ( args [ 4 ] ) ;
vecMax . y = atof ( args [ 5 ] ) ;
vecMax . z = atof ( args [ 6 ] ) ;
}
else
{
return ;
}
float flTime = 10.0f ;
Vector vecPoints [ 8 ] ;
vecPoints [ 0 ] . Init ( vecMin . x , vecMin . y , vecMin . z ) ;
vecPoints [ 1 ] . Init ( vecMin . x , vecMax . y , vecMin . z ) ;
vecPoints [ 2 ] . Init ( vecMax . x , vecMax . y , vecMin . z ) ;
vecPoints [ 3 ] . Init ( vecMax . x , vecMin . y , vecMin . z ) ;
vecPoints [ 4 ] . Init ( vecMin . x , vecMin . y , vecMax . z ) ;
vecPoints [ 5 ] . Init ( vecMin . x , vecMax . y , vecMax . z ) ;
vecPoints [ 6 ] . Init ( vecMax . x , vecMax . y , vecMax . z ) ;
vecPoints [ 7 ] . Init ( vecMax . x , vecMin . y , vecMax . z ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 0 ] , vecPoints [ 1 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 1 ] , vecPoints [ 2 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 2 ] , vecPoints [ 3 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 3 ] , vecPoints [ 0 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 4 ] , vecPoints [ 5 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 5 ] , vecPoints [ 6 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 6 ] , vecPoints [ 7 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 7 ] , vecPoints [ 4 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 0 ] , vecPoints [ 4 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 3 ] , vecPoints [ 7 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 1 ] , vecPoints [ 5 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 2 ] , vecPoints [ 6 ] , 255 , 0 , 0 , true , flTime ) ;
Msg ( " VoxelTreeBox - (%f %f %f) to (%f %f %f) \n " , vecMin . x , vecMin . y , vecMin . z , vecMax . x , vecMax . y , vecMax . z ) ;
partition - > RenderObjectsInBox ( vecMin , vecMax , flTime ) ;
}
static ConCommand voxeltree_box ( " voxeltree_box " , CC_VoxelTreeBox , " View entities in the voxel-tree inside box <Vector(min) , Vector ( max ) > . " , FCVAR_CHEAT ) ;
void CC_VoxelTreeSphere ( const CCommand & args )
{
Vector vecCenter ;
float flRadius ;
if ( args . ArgC ( ) > = 4 )
{
vecCenter . x = atof ( args [ 1 ] ) ;
vecCenter . y = atof ( args [ 2 ] ) ;
vecCenter . z = atof ( args [ 3 ] ) ;
flRadius = atof ( args [ 3 ] ) ;
}
else
{
return ;
}
float flTime = 3.0f ;
Vector vecMin , vecMax ;
vecMin . Init ( vecCenter . x - flRadius , vecCenter . y - flRadius , vecCenter . z - flRadius ) ;
vecMax . Init ( vecCenter . x + flRadius , vecCenter . y + flRadius , vecCenter . z + flRadius ) ;
Vector vecPoints [ 8 ] ;
vecPoints [ 0 ] . Init ( vecMin . x , vecMin . y , vecMin . z ) ;
vecPoints [ 1 ] . Init ( vecMin . x , vecMax . y , vecMin . z ) ;
vecPoints [ 2 ] . Init ( vecMax . x , vecMax . y , vecMin . z ) ;
vecPoints [ 3 ] . Init ( vecMax . x , vecMin . y , vecMin . z ) ;
vecPoints [ 4 ] . Init ( vecMin . x , vecMin . y , vecMax . z ) ;
vecPoints [ 5 ] . Init ( vecMin . x , vecMax . y , vecMax . z ) ;
vecPoints [ 6 ] . Init ( vecMax . x , vecMax . y , vecMax . z ) ;
vecPoints [ 7 ] . Init ( vecMax . x , vecMin . y , vecMax . z ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 0 ] , vecPoints [ 1 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 1 ] , vecPoints [ 2 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 2 ] , vecPoints [ 3 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 3 ] , vecPoints [ 0 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 4 ] , vecPoints [ 5 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 5 ] , vecPoints [ 6 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 6 ] , vecPoints [ 7 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 7 ] , vecPoints [ 4 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 0 ] , vecPoints [ 4 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 3 ] , vecPoints [ 7 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 1 ] , vecPoints [ 5 ] , 255 , 0 , 0 , true , flTime ) ;
debugoverlay - > AddLineOverlay ( vecPoints [ 2 ] , vecPoints [ 6 ] , 255 , 0 , 0 , true , flTime ) ;
Msg ( " VoxelTreeSphere - (%f %f %f), %f \n " , vecCenter . x , vecCenter . y , vecCenter . z , flRadius ) ;
partition - > RenderObjectsInSphere ( vecCenter , flRadius , flTime ) ;
}
static ConCommand voxeltree_sphere ( " voxeltree_sphere " , CC_VoxelTreeSphere , " View entities in the voxel-tree inside sphere <Vector(center) , float ( radius ) > . " , FCVAR_CHEAT ) ;
# define NUM_COLLISION_TESTS 2500
void CC_CollisionTest ( const CCommand & args )
{
if ( ! physenv )
return ;
Msg ( " Testing collision system \n " ) ;
partition - > ReportStats ( " " ) ;
int i ;
CBaseEntity * pSpot = gEntList . FindEntityByClassname ( NULL , " info_player_start " ) ;
Vector start = pSpot - > GetAbsOrigin ( ) ;
static Vector * targets = NULL ;
static bool first = true ;
static float test [ 2 ] = { 1 , 1 } ;
if ( first )
{
targets = new Vector [ NUM_COLLISION_TESTS ] ;
float radius = 0 ;
float theta = 0 ;
float phi = 0 ;
for ( i = 0 ; i < NUM_COLLISION_TESTS ; i + + )
{
radius + = NUM_COLLISION_TESTS * 123.123 ;
radius = fabs ( fmod ( radius , 128 ) ) ;
theta + = NUM_COLLISION_TESTS * 76.76 ;
theta = fabs ( fmod ( theta , DEG2RAD ( 360 ) ) ) ;
phi + = NUM_COLLISION_TESTS * 1997.99 ;
phi = fabs ( fmod ( phi , DEG2RAD ( 180 ) ) ) ;
float st , ct , sp , cp ;
SinCos ( theta , & st , & ct ) ;
SinCos ( phi , & sp , & cp ) ;
targets [ i ] . x = radius * ct * sp ;
targets [ i ] . y = radius * st * sp ;
targets [ i ] . z = radius * cp ;
// make the trace 1024 units long
Vector dir = targets [ i ] - start ;
VectorNormalize ( dir ) ;
targets [ i ] = start + dir * 1024 ;
}
first = false ;
}
//Vector results[NUM_COLLISION_TESTS];
int testType = 0 ;
if ( args . ArgC ( ) > = 2 )
{
testType = atoi ( args [ 1 ] ) ;
}
float duration = 0 ;
Vector size [ 2 ] ;
size [ 0 ] . Init ( 0 , 0 , 0 ) ;
size [ 1 ] . Init ( 16 , 16 , 16 ) ;
unsigned int dots = 0 ;
int nMask = MASK_ALL & ~ ( CONTENTS_MONSTER | CONTENTS_HITBOX ) ;
for ( int j = 0 ; j < 2 ; j + + )
{
float startTime = engine - > Time ( ) ;
if ( testType = = 1 )
{
trace_t tr ;
for ( i = 0 ; i < NUM_COLLISION_TESTS ; i + + )
{
UTIL_TraceHull ( start , targets [ i ] , - size [ 1 ] , size [ 1 ] , nMask , NULL , COLLISION_GROUP_NONE , & tr ) ;
}
}
else
{
testType = 0 ;
trace_t tr ;
for ( i = 0 ; i < NUM_COLLISION_TESTS ; i + + )
{
if ( i = = 0 )
{
partition - > RenderLeafsForRayTraceStart ( 10.0f ) ;
}
UTIL_TraceLine ( start , targets [ i ] , nMask , NULL , COLLISION_GROUP_NONE , & tr ) ;
if ( i = = 0 )
{
partition - > RenderLeafsForRayTraceEnd ( ) ;
}
}
}
duration + = engine - > Time ( ) - startTime ;
}
test [ testType ] = duration ;
Msg ( " %d collisions in %.2f ms (%u dots) \n " , NUM_COLLISION_TESTS , duration * 1000 , dots ) ;
partition - > ReportStats ( " " ) ;
# if 1
int red = 255 , green = 0 , blue = 0 ;
for ( i = 0 ; i < 1 /*NUM_COLLISION_TESTS*/ ; i + + )
{
NDebugOverlay : : Line ( start , targets [ i ] , red , green , blue , false , 2 ) ;
}
# endif
}
static ConCommand collision_test ( " collision_test " , CC_CollisionTest , " Tests collision system " , FCVAR_CHEAT ) ;