aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/shared/baseentity_shared.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/shared/baseentity_shared.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/shared/baseentity_shared.cpp')
-rw-r--r--mp/src/game/shared/baseentity_shared.cpp5116
1 files changed, 2558 insertions, 2558 deletions
diff --git a/mp/src/game/shared/baseentity_shared.cpp b/mp/src/game/shared/baseentity_shared.cpp
index 949143b1..43348cb1 100644
--- a/mp/src/game/shared/baseentity_shared.cpp
+++ b/mp/src/game/shared/baseentity_shared.cpp
@@ -1,2558 +1,2558 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-
-#include "decals.h"
-#include "effect_dispatch_data.h"
-#include "model_types.h"
-#include "gamestringpool.h"
-#include "ammodef.h"
-#include "takedamageinfo.h"
-#include "shot_manipulator.h"
-#include "ai_debug_shared.h"
-#include "mapentities_shared.h"
-#include "debugoverlay_shared.h"
-#include "coordsize.h"
-#include "vphysics/performance.h"
-
-#ifdef CLIENT_DLL
- #include "c_te_effect_dispatch.h"
-#else
- #include "te_effect_dispatch.h"
- #include "soundent.h"
- #include "iservervehicle.h"
- #include "player_pickup.h"
- #include "waterbullet.h"
- #include "func_break.h"
-
-#ifdef HL2MP
- #include "te_hl2mp_shotgun_shot.h"
-#endif
-
- #include "gamestats.h"
-
-#endif
-
-#ifdef HL2_EPISODIC
-ConVar hl2_episodic( "hl2_episodic", "1", FCVAR_REPLICATED );
-#else
-ConVar hl2_episodic( "hl2_episodic", "0", FCVAR_REPLICATED );
-#endif//HL2_EPISODIC
-
-#ifdef PORTAL
- #include "prop_portal_shared.h"
-#endif
-
-#ifdef TF_DLL
-#include "tf_gamerules.h"
-#include "tf_weaponbase.h"
-#endif // TF_DLL
-
-#include "rumble_shared.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-#ifdef GAME_DLL
- ConVar ent_debugkeys( "ent_debugkeys", "" );
- extern bool ParseKeyvalue( void *pObject, typedescription_t *pFields, int iNumFields, const char *szKeyName, const char *szValue );
- extern bool ExtractKeyvalue( void *pObject, typedescription_t *pFields, int iNumFields, const char *szKeyName, char *szValue, int iMaxLen );
-#endif
-
-bool CBaseEntity::m_bAllowPrecache = false;
-
-// Set default max values for entities based on the existing constants from elsewhere
-float k_flMaxEntityPosCoord = MAX_COORD_FLOAT;
-float k_flMaxEntityEulerAngle = 360.0 * 1000.0f; // really should be restricted to +/-180, but some code doesn't adhere to this. let's just trap NANs, etc
-// Sometimes the resulting computed speeds are legitimately above the original
-// constants; use bumped up versions for the downstream validation logic to
-// account for this.
-float k_flMaxEntitySpeed = k_flMaxVelocity * 2.0f;
-float k_flMaxEntitySpinRate = k_flMaxAngularVelocity * 10.0f;
-
-ConVar ai_shot_bias_min( "ai_shot_bias_min", "-1.0", FCVAR_REPLICATED );
-ConVar ai_shot_bias_max( "ai_shot_bias_max", "1.0", FCVAR_REPLICATED );
-ConVar ai_debug_shoot_positions( "ai_debug_shoot_positions", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
-
-// Utility func to throttle rate at which the "reasonable position" spew goes out
-static double s_LastEntityReasonableEmitTime;
-bool CheckEmitReasonablePhysicsSpew()
-{
-
- // Reported recently?
- double now = Plat_FloatTime();
- if ( now >= s_LastEntityReasonableEmitTime && now < s_LastEntityReasonableEmitTime + 5.0 )
- {
- // Already reported recently
- return false;
- }
-
- // Not reported recently. Report it now
- s_LastEntityReasonableEmitTime = now;
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Spawn some blood particles
-//-----------------------------------------------------------------------------
-void SpawnBlood(Vector vecSpot, const Vector &vecDir, int bloodColor, float flDamage)
-{
- UTIL_BloodDrips( vecSpot, vecDir, bloodColor, (int)flDamage );
-}
-
-#if !defined( NO_ENTITY_PREDICTION )
-//-----------------------------------------------------------------------------
-// The player drives simulation of this entity
-//-----------------------------------------------------------------------------
-void CBaseEntity::SetPlayerSimulated( CBasePlayer *pOwner )
-{
- m_bIsPlayerSimulated = true;
- pOwner->AddToPlayerSimulationList( this );
- m_hPlayerSimulationOwner = pOwner;
-}
-
-void CBaseEntity::UnsetPlayerSimulated( void )
-{
- if ( m_hPlayerSimulationOwner != NULL )
- {
- m_hPlayerSimulationOwner->RemoveFromPlayerSimulationList( this );
- }
- m_hPlayerSimulationOwner = NULL;
- m_bIsPlayerSimulated = false;
-}
-#endif
-
-// position of eyes
-Vector CBaseEntity::EyePosition( void )
-{
- return GetAbsOrigin() + GetViewOffset();
-}
-
-const QAngle &CBaseEntity::EyeAngles( void )
-{
- return GetAbsAngles();
-}
-
-const QAngle &CBaseEntity::LocalEyeAngles( void )
-{
- return GetLocalAngles();
-}
-
-// position of ears
-Vector CBaseEntity::EarPosition( void )
-{
- return EyePosition();
-}
-
-void CBaseEntity::SetViewOffset( const Vector& v )
-{
- m_vecViewOffset = v;
-}
-
-const Vector& CBaseEntity::GetViewOffset() const
-{
- return m_vecViewOffset;
-}
-
-
-//-----------------------------------------------------------------------------
-// center point of entity
-//-----------------------------------------------------------------------------
-const Vector &CBaseEntity::WorldSpaceCenter( ) const
-{
- return CollisionProp()->WorldSpaceCenter();
-}
-
-#if !defined( CLIENT_DLL )
-#define CHANGE_FLAGS(flags,newFlags) { unsigned int old = flags; flags = (newFlags); gEntList.ReportEntityFlagsChanged( this, old, flags ); }
-#else
-#define CHANGE_FLAGS(flags,newFlags) (flags = (newFlags))
-#endif
-
-void CBaseEntity::AddFlag( int flags )
-{
- CHANGE_FLAGS( m_fFlags, m_fFlags | flags );
-}
-
-void CBaseEntity::RemoveFlag( int flagsToRemove )
-{
- CHANGE_FLAGS( m_fFlags, m_fFlags & ~flagsToRemove );
-}
-
-void CBaseEntity::ClearFlags( void )
-{
- CHANGE_FLAGS( m_fFlags, 0 );
-}
-
-void CBaseEntity::ToggleFlag( int flagToToggle )
-{
- CHANGE_FLAGS( m_fFlags, m_fFlags ^ flagToToggle );
-}
-
-void CBaseEntity::SetEffects( int nEffects )
-{
- if ( nEffects != m_fEffects )
- {
-#if !defined( CLIENT_DLL )
-#ifdef HL2_EPISODIC
- // Hack for now, to avoid player emitting radius with his flashlight
- if ( !IsPlayer() )
- {
- if ( (nEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) && !(m_fEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) )
- {
- AddEntityToDarknessCheck( this );
- }
- else if ( !(nEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) && (m_fEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) )
- {
- RemoveEntityFromDarknessCheck( this );
- }
- }
-#endif // HL2_EPISODIC
-#endif // !CLIENT_DLL
-
- m_fEffects = nEffects;
-
-#ifndef CLIENT_DLL
- DispatchUpdateTransmitState();
-#else
- UpdateVisibility();
-#endif
- }
-}
-
-void CBaseEntity::AddEffects( int nEffects )
-{
-#if !defined( CLIENT_DLL )
-#ifdef HL2_EPISODIC
- if ( (nEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) && !(m_fEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) )
- {
- // Hack for now, to avoid player emitting radius with his flashlight
- if ( !IsPlayer() )
- {
- AddEntityToDarknessCheck( this );
- }
- }
-#endif // HL2_EPISODIC
-#endif // !CLIENT_DLL
-
- m_fEffects |= nEffects;
-
- if ( nEffects & EF_NODRAW)
- {
-#ifndef CLIENT_DLL
- DispatchUpdateTransmitState();
-#else
- UpdateVisibility();
-#endif
- }
-}
-
-void CBaseEntity::SetBlocksLOS( bool bBlocksLOS )
-{
- if ( bBlocksLOS )
- {
- RemoveEFlags( EFL_DONTBLOCKLOS );
- }
- else
- {
- AddEFlags( EFL_DONTBLOCKLOS );
- }
-}
-
-bool CBaseEntity::BlocksLOS( void )
-{
- return !IsEFlagSet(EFL_DONTBLOCKLOS);
-}
-
-void CBaseEntity::SetAIWalkable( bool bBlocksLOS )
-{
- if ( bBlocksLOS )
- {
- RemoveEFlags( EFL_DONTWALKON );
- }
- else
- {
- AddEFlags( EFL_DONTWALKON );
- }
-}
-
-bool CBaseEntity::IsAIWalkable( void )
-{
- return !IsEFlagSet(EFL_DONTWALKON);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Handles keys and outputs from the BSP.
-// Input : mapData - Text block of keys and values from the BSP.
-//-----------------------------------------------------------------------------
-void CBaseEntity::ParseMapData( CEntityMapData *mapData )
-{
- char keyName[MAPKEY_MAXLENGTH];
- char value[MAPKEY_MAXLENGTH];
-
- #ifdef _DEBUG
- #ifdef GAME_DLL
- ValidateDataDescription();
- #endif // GAME_DLL
- #endif // _DEBUG
-
- // loop through all keys in the data block and pass the info back into the object
- if ( mapData->GetFirstKey(keyName, value) )
- {
- do
- {
- KeyValue( keyName, value );
- }
- while ( mapData->GetNextKey(keyName, value) );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Parse data from a map file
-//-----------------------------------------------------------------------------
-bool CBaseEntity::KeyValue( const char *szKeyName, const char *szValue )
-{
- //!! temp hack, until worldcraft is fixed
- // strip the # tokens from (duplicate) key names
- char *s = (char *)strchr( szKeyName, '#' );
- if ( s )
- {
- *s = '\0';
- }
-
- if ( FStrEq( szKeyName, "rendercolor" ) || FStrEq( szKeyName, "rendercolor32" ))
- {
- color32 tmp;
- UTIL_StringToColor32( &tmp, szValue );
- SetRenderColor( tmp.r, tmp.g, tmp.b );
- // don't copy alpha, legacy support uses renderamt
- return true;
- }
-
- if ( FStrEq( szKeyName, "renderamt" ) )
- {
- SetRenderColorA( atoi( szValue ) );
- return true;
- }
-
- if ( FStrEq( szKeyName, "disableshadows" ))
- {
- int val = atoi( szValue );
- if (val)
- {
- AddEffects( EF_NOSHADOW );
- }
- return true;
- }
-
- if ( FStrEq( szKeyName, "mins" ))
- {
- Vector mins;
- UTIL_StringToVector( mins.Base(), szValue );
- CollisionProp()->SetCollisionBounds( mins, CollisionProp()->OBBMaxs() );
- return true;
- }
-
- if ( FStrEq( szKeyName, "maxs" ))
- {
- Vector maxs;
- UTIL_StringToVector( maxs.Base(), szValue );
- CollisionProp()->SetCollisionBounds( CollisionProp()->OBBMins(), maxs );
- return true;
- }
-
- if ( FStrEq( szKeyName, "disablereceiveshadows" ))
- {
- int val = atoi( szValue );
- if (val)
- {
- AddEffects( EF_NORECEIVESHADOW );
- }
- return true;
- }
-
- if ( FStrEq( szKeyName, "nodamageforces" ))
- {
- int val = atoi( szValue );
- if (val)
- {
- AddEFlags( EFL_NO_DAMAGE_FORCES );
- }
- return true;
- }
-
- // Fix up single angles
- if( FStrEq( szKeyName, "angle" ) )
- {
- static char szBuf[64];
-
- float y = atof( szValue );
- if (y >= 0)
- {
- Q_snprintf( szBuf,sizeof(szBuf), "%f %f %f", GetLocalAngles()[0], y, GetLocalAngles()[2] );
- }
- else if ((int)y == -1)
- {
- Q_strncpy( szBuf, "-90 0 0", sizeof(szBuf) );
- }
- else
- {
- Q_strncpy( szBuf, "90 0 0", sizeof(szBuf) );
- }
-
- // Do this so inherited classes looking for 'angles' don't have to bother with 'angle'
- return KeyValue( szKeyName, szBuf );
- }
-
- // NOTE: Have to do these separate because they set two values instead of one
- if( FStrEq( szKeyName, "angles" ) )
- {
- QAngle angles;
- UTIL_StringToVector( angles.Base(), szValue );
-
- // If you're hitting this assert, it's probably because you're
- // calling SetLocalAngles from within a KeyValues method.. use SetAbsAngles instead!
- Assert( (GetMoveParent() == NULL) && !IsEFlagSet( EFL_DIRTY_ABSTRANSFORM ) );
- SetAbsAngles( angles );
- return true;
- }
-
- if( FStrEq( szKeyName, "origin" ) )
- {
- Vector vecOrigin;
- UTIL_StringToVector( vecOrigin.Base(), szValue );
-
- // If you're hitting this assert, it's probably because you're
- // calling SetLocalOrigin from within a KeyValues method.. use SetAbsOrigin instead!
- Assert( (GetMoveParent() == NULL) && !IsEFlagSet( EFL_DIRTY_ABSTRANSFORM ) );
- SetAbsOrigin( vecOrigin );
- return true;
- }
-
-#ifdef GAME_DLL
-
- if ( FStrEq( szKeyName, "targetname" ) )
- {
- m_iName = AllocPooledString( szValue );
- return true;
- }
-
- // loop through the data description, and try and place the keys in
- if ( !*ent_debugkeys.GetString() )
- {
- for ( datamap_t *dmap = GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
- {
- if ( ::ParseKeyvalue(this, dmap->dataDesc, dmap->dataNumFields, szKeyName, szValue) )
- return true;
- }
- }
- else
- {
- // debug version - can be used to see what keys have been parsed in
- bool printKeyHits = false;
- const char *debugName = "";
-
- if ( *ent_debugkeys.GetString() && !Q_stricmp(ent_debugkeys.GetString(), STRING(m_iClassname)) )
- {
- // Msg( "-- found entity of type %s\n", STRING(m_iClassname) );
- printKeyHits = true;
- debugName = STRING(m_iClassname);
- }
-
- // loop through the data description, and try and place the keys in
- for ( datamap_t *dmap = GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
- {
- if ( !printKeyHits && *ent_debugkeys.GetString() && !Q_stricmp(dmap->dataClassName, ent_debugkeys.GetString()) )
- {
- // Msg( "-- found class of type %s\n", dmap->dataClassName );
- printKeyHits = true;
- debugName = dmap->dataClassName;
- }
-
- if ( ::ParseKeyvalue(this, dmap->dataDesc, dmap->dataNumFields, szKeyName, szValue) )
- {
- if ( printKeyHits )
- Msg( "(%s) key: %-16s value: %s\n", debugName, szKeyName, szValue );
-
- return true;
- }
- }
-
- if ( printKeyHits )
- Msg( "!! (%s) key not handled: \"%s\" \"%s\"\n", STRING(m_iClassname), szKeyName, szValue );
- }
-
-#endif
-
- // key hasn't been handled
- return false;
-}
-
-bool CBaseEntity::KeyValue( const char *szKeyName, float flValue )
-{
- char string[256];
-
- Q_snprintf(string,sizeof(string), "%f", flValue );
-
- return KeyValue( szKeyName, string );
-}
-
-bool CBaseEntity::KeyValue( const char *szKeyName, const Vector &vecValue )
-{
- char string[256];
-
- Q_snprintf(string,sizeof(string), "%f %f %f", vecValue.x, vecValue.y, vecValue.z );
-
- return KeyValue( szKeyName, string );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-
-bool CBaseEntity::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen )
-{
- if ( FStrEq( szKeyName, "rendercolor" ) || FStrEq( szKeyName, "rendercolor32" ))
- {
- color32 tmp = GetRenderColor();
- Q_snprintf( szValue, iMaxLen, "%d %d %d %d", tmp.r, tmp.g, tmp.b, tmp.a );
- return true;
- }
-
- if ( FStrEq( szKeyName, "renderamt" ) )
- {
- color32 tmp = GetRenderColor();
- Q_snprintf( szValue, iMaxLen, "%d", tmp.a );
- return true;
- }
-
- if ( FStrEq( szKeyName, "disableshadows" ))
- {
- Q_snprintf( szValue, iMaxLen, "%d", IsEffectActive( EF_NOSHADOW ) );
- return true;
- }
-
- if ( FStrEq( szKeyName, "mins" ))
- {
- Assert( 0 );
- return false;
- }
-
- if ( FStrEq( szKeyName, "maxs" ))
- {
- Assert( 0 );
- return false;
- }
-
- if ( FStrEq( szKeyName, "disablereceiveshadows" ))
- {
- Q_snprintf( szValue, iMaxLen, "%d", IsEffectActive( EF_NORECEIVESHADOW ) );
- return true;
- }
-
- if ( FStrEq( szKeyName, "nodamageforces" ))
- {
- Q_snprintf( szValue, iMaxLen, "%d", IsEffectActive( EFL_NO_DAMAGE_FORCES ) );
- return true;
- }
-
- // Fix up single angles
- if( FStrEq( szKeyName, "angle" ) )
- {
- return false;
- }
-
- // NOTE: Have to do these separate because they set two values instead of one
- if( FStrEq( szKeyName, "angles" ) )
- {
- QAngle angles = GetAbsAngles();
-
- Q_snprintf( szValue, iMaxLen, "%f %f %f", angles.x, angles.y, angles.z );
- return true;
- }
-
- if( FStrEq( szKeyName, "origin" ) )
- {
- Vector vecOrigin = GetAbsOrigin();
- Q_snprintf( szValue, iMaxLen, "%f %f %f", vecOrigin.x, vecOrigin.y, vecOrigin.z );
- return true;
- }
-
-#ifdef GAME_DLL
-
- if ( FStrEq( szKeyName, "targetname" ) )
- {
- Q_snprintf( szValue, iMaxLen, "%s", STRING( GetEntityName() ) );
- return true;
- }
-
- if ( FStrEq( szKeyName, "classname" ) )
- {
- Q_snprintf( szValue, iMaxLen, "%s", GetClassname() );
- return true;
- }
-
- for ( datamap_t *dmap = GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
- {
- if ( ::ExtractKeyvalue( this, dmap->dataDesc, dmap->dataNumFields, szKeyName, szValue, iMaxLen ) )
- return true;
- }
-#endif
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : collisionGroup -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseEntity::ShouldCollide( int collisionGroup, int contentsMask ) const
-{
- if ( m_CollisionGroup == COLLISION_GROUP_DEBRIS )
- {
- if ( ! (contentsMask & CONTENTS_DEBRIS) )
- return false;
- }
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : seed -
-//-----------------------------------------------------------------------------
-void CBaseEntity::SetPredictionRandomSeed( const CUserCmd *cmd )
-{
- if ( !cmd )
- {
- m_nPredictionRandomSeed = -1;
- return;
- }
-
- m_nPredictionRandomSeed = ( cmd->random_seed );
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose : Base implimentation for entity handling decals
-//------------------------------------------------------------------------------
-void CBaseEntity::DecalTrace( trace_t *pTrace, char const *decalName )
-{
- int index = decalsystem->GetDecalIndexForName( decalName );
- if ( index < 0 )
- return;
-
- Assert( pTrace->m_pEnt );
-
- CBroadcastRecipientFilter filter;
- te->Decal( filter, 0.0, &pTrace->endpos, &pTrace->startpos,
- pTrace->GetEntityIndex(), pTrace->hitbox, index );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Base handling for impacts against entities
-//-----------------------------------------------------------------------------
-void CBaseEntity::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
-{
- VPROF( "CBaseEntity::ImpactTrace" );
- Assert( pTrace->m_pEnt );
-
- CBaseEntity *pEntity = pTrace->m_pEnt;
-
- // Build the impact data
- CEffectData data;
- data.m_vOrigin = pTrace->endpos;
- data.m_vStart = pTrace->startpos;
- data.m_nSurfaceProp = pTrace->surface.surfaceProps;
- data.m_nDamageType = iDamageType;
- data.m_nHitBox = pTrace->hitbox;
-#ifdef CLIENT_DLL
- data.m_hEntity = ClientEntityList().EntIndexToHandle( pEntity->entindex() );
-#else
- data.m_nEntIndex = pEntity->entindex();
-#endif
-
- // Send it on its way
- if ( !pCustomImpactName )
- {
- DispatchEffect( "Impact", data );
- }
- else
- {
- DispatchEffect( pCustomImpactName, data );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: returns the damage decal to use, given a damage type
-// Input : bitsDamageType - the damage type
-// Output : the index of the damage decal to use
-//-----------------------------------------------------------------------------
-char const *CBaseEntity::DamageDecal( int bitsDamageType, int gameMaterial )
-{
- if ( m_nRenderMode == kRenderTransAlpha )
- return "";
-
- if ( m_nRenderMode != kRenderNormal && gameMaterial == 'G' )
- return "BulletProof";
-
- if ( bitsDamageType == DMG_SLASH )
- return "ManhackCut";
-
- // This will get translated at a lower layer based on game material
- return "Impact.Concrete";
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CBaseEntity::GetIndexForThinkContext( const char *pszContext )
-{
- for ( int i = 0; i < m_aThinkFunctions.Size(); i++ )
- {
- if ( !Q_strncmp( STRING( m_aThinkFunctions[i].m_iszContext ), pszContext, MAX_CONTEXT_LENGTH ) )
- return i;
- }
-
- return NO_THINK_CONTEXT;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get a fresh think context for this entity
-//-----------------------------------------------------------------------------
-int CBaseEntity::RegisterThinkContext( const char *szContext )
-{
- int iIndex = GetIndexForThinkContext( szContext );
- if ( iIndex != NO_THINK_CONTEXT )
- return iIndex;
-
- // Make a new think func
- thinkfunc_t sNewFunc;
- Q_memset( &sNewFunc, 0, sizeof( sNewFunc ) );
- sNewFunc.m_pfnThink = NULL;
- sNewFunc.m_nNextThinkTick = 0;
- sNewFunc.m_iszContext = AllocPooledString(szContext);
-
- // Insert it into our list
- return m_aThinkFunctions.AddToTail( sNewFunc );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-BASEPTR CBaseEntity::ThinkSet( BASEPTR func, float thinkTime, const char *szContext )
-{
-#if !defined( CLIENT_DLL )
-#ifdef _DEBUG
-#ifdef GNUC
- COMPILE_TIME_ASSERT( sizeof(func) == 8 );
-#else
- COMPILE_TIME_ASSERT( sizeof(func) == 4 );
-#endif
-#endif
-#endif
-
- // Old system?
- if ( !szContext )
- {
- m_pfnThink = func;
-#if !defined( CLIENT_DLL )
-#ifdef _DEBUG
- FunctionCheck( *(reinterpret_cast<void **>(&m_pfnThink)), "BaseThinkFunc" );
-#endif
-#endif
- return m_pfnThink;
- }
-
- // Find the think function in our list, and if we couldn't find it, register it
- int iIndex = GetIndexForThinkContext( szContext );
- if ( iIndex == NO_THINK_CONTEXT )
- {
- iIndex = RegisterThinkContext( szContext );
- }
-
- m_aThinkFunctions[ iIndex ].m_pfnThink = func;
-#if !defined( CLIENT_DLL )
-#ifdef _DEBUG
- FunctionCheck( *(reinterpret_cast<void **>(&m_aThinkFunctions[ iIndex ].m_pfnThink)), szContext );
-#endif
-#endif
-
- if ( thinkTime != 0 )
- {
- int thinkTick = ( thinkTime == TICK_NEVER_THINK ) ? TICK_NEVER_THINK : TIME_TO_TICKS( thinkTime );
- m_aThinkFunctions[ iIndex ].m_nNextThinkTick = thinkTick;
- CheckHasThinkFunction( thinkTick == TICK_NEVER_THINK ? false : true );
- }
- return func;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBaseEntity::SetNextThink( float thinkTime, const char *szContext )
-{
- int thinkTick = ( thinkTime == TICK_NEVER_THINK ) ? TICK_NEVER_THINK : TIME_TO_TICKS( thinkTime );
-
- // Are we currently in a think function with a context?
- int iIndex = 0;
- if ( !szContext )
- {
-#ifdef _DEBUG
- if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
- {
- Msg( "Warning: Setting base think function within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
- }
-#endif
-
- // Old system
- m_nNextThinkTick = thinkTick;
- CheckHasThinkFunction( thinkTick == TICK_NEVER_THINK ? false : true );
- return;
- }
- else
- {
- // Find the think function in our list, and if we couldn't find it, register it
- iIndex = GetIndexForThinkContext( szContext );
- if ( iIndex == NO_THINK_CONTEXT )
- {
- iIndex = RegisterThinkContext( szContext );
- }
- }
-
- // Old system
- m_aThinkFunctions[ iIndex ].m_nNextThinkTick = thinkTick;
- CheckHasThinkFunction( thinkTick == TICK_NEVER_THINK ? false : true );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-float CBaseEntity::GetNextThink( const char *szContext )
-{
- // Are we currently in a think function with a context?
- int iIndex = 0;
- if ( !szContext )
- {
-#ifdef _DEBUG
- if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
- {
- Msg( "Warning: Getting base nextthink time within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
- }
-#endif
-
- if ( m_nNextThinkTick == TICK_NEVER_THINK )
- return TICK_NEVER_THINK;
-
- // Old system
- return TICK_INTERVAL * (m_nNextThinkTick );
- }
- else
- {
- // Find the think function in our list
- iIndex = GetIndexForThinkContext( szContext );
- }
-
- if ( iIndex == m_aThinkFunctions.InvalidIndex() )
- return TICK_NEVER_THINK;
-
- if ( m_aThinkFunctions[ iIndex ].m_nNextThinkTick == TICK_NEVER_THINK )
- {
- return TICK_NEVER_THINK;
- }
- return TICK_INTERVAL * (m_aThinkFunctions[ iIndex ].m_nNextThinkTick );
-}
-
-int CBaseEntity::GetNextThinkTick( const char *szContext /*= NULL*/ )
-{
- // Are we currently in a think function with a context?
- int iIndex = 0;
- if ( !szContext )
- {
-#ifdef _DEBUG
- if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
- {
- Msg( "Warning: Getting base nextthink time within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
- }
-#endif
-
- if ( m_nNextThinkTick == TICK_NEVER_THINK )
- return TICK_NEVER_THINK;
-
- // Old system
- return m_nNextThinkTick;
- }
- else
- {
- // Find the think function in our list
- iIndex = GetIndexForThinkContext( szContext );
-
- // Looking up an invalid think context!
- Assert( iIndex != -1 );
- }
-
- if ( ( iIndex == -1 ) || ( m_aThinkFunctions[ iIndex ].m_nNextThinkTick == TICK_NEVER_THINK ) )
- {
- return TICK_NEVER_THINK;
- }
-
- return m_aThinkFunctions[ iIndex ].m_nNextThinkTick;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-float CBaseEntity::GetLastThink( const char *szContext )
-{
- // Are we currently in a think function with a context?
- int iIndex = 0;
- if ( !szContext )
- {
-#ifdef _DEBUG
- if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
- {
- Msg( "Warning: Getting base lastthink time within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
- }
-#endif
- // Old system
- return m_nLastThinkTick * TICK_INTERVAL;
- }
- else
- {
- // Find the think function in our list
- iIndex = GetIndexForThinkContext( szContext );
- }
-
- return m_aThinkFunctions[ iIndex ].m_nLastThinkTick * TICK_INTERVAL;
-}
-
-int CBaseEntity::GetLastThinkTick( const char *szContext /*= NULL*/ )
-{
- // Are we currently in a think function with a context?
- int iIndex = 0;
- if ( !szContext )
- {
-#ifdef _DEBUG
- if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
- {
- Msg( "Warning: Getting base lastthink time within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
- }
-#endif
- // Old system
- return m_nLastThinkTick;
- }
- else
- {
- // Find the think function in our list
- iIndex = GetIndexForThinkContext( szContext );
- }
-
- return m_aThinkFunctions[ iIndex ].m_nLastThinkTick;
-}
-
-bool CBaseEntity::WillThink()
-{
- if ( m_nNextThinkTick > 0 )
- return true;
-
- for ( int i = 0; i < m_aThinkFunctions.Count(); i++ )
- {
- if ( m_aThinkFunctions[i].m_nNextThinkTick > 0 )
- return true;
- }
-
- return false;
-}
-
-// returns the first tick the entity will run any think function
-// returns TICK_NEVER_THINK if no think functions are scheduled
-int CBaseEntity::GetFirstThinkTick()
-{
- int minTick = TICK_NEVER_THINK;
- if ( m_nNextThinkTick > 0 )
- {
- minTick = m_nNextThinkTick;
- }
-
- for ( int i = 0; i < m_aThinkFunctions.Count(); i++ )
- {
- int next = m_aThinkFunctions[i].m_nNextThinkTick;
- if ( next > 0 )
- {
- if ( next < minTick || minTick == TICK_NEVER_THINK )
- {
- minTick = next;
- }
- }
- }
- return minTick;
-}
-
-// NOTE: pass in the isThinking hint so we have to search the think functions less
-void CBaseEntity::CheckHasThinkFunction( bool isThinking )
-{
- if ( IsEFlagSet( EFL_NO_THINK_FUNCTION ) && isThinking )
- {
- RemoveEFlags( EFL_NO_THINK_FUNCTION );
- }
- else if ( !isThinking && !IsEFlagSet( EFL_NO_THINK_FUNCTION ) && !WillThink() )
- {
- AddEFlags( EFL_NO_THINK_FUNCTION );
- }
-#if !defined( CLIENT_DLL )
- SimThink_EntityChanged( this );
-#endif
-}
-
-bool CBaseEntity::WillSimulateGamePhysics()
-{
- // players always simulate game physics
- if ( !IsPlayer() )
- {
- MoveType_t movetype = GetMoveType();
-
- if ( movetype == MOVETYPE_NONE || movetype == MOVETYPE_VPHYSICS )
- return false;
-
-#if !defined( CLIENT_DLL )
- // MOVETYPE_PUSH not supported on the client
- if ( movetype == MOVETYPE_PUSH && GetMoveDoneTime() <= 0 )
- return false;
-#endif
- }
-
- return true;
-}
-
-void CBaseEntity::CheckHasGamePhysicsSimulation()
-{
- bool isSimulating = WillSimulateGamePhysics();
- if ( isSimulating != IsEFlagSet(EFL_NO_GAME_PHYSICS_SIMULATION) )
- return;
- if ( isSimulating )
- {
- RemoveEFlags( EFL_NO_GAME_PHYSICS_SIMULATION );
- }
- else
- {
- AddEFlags( EFL_NO_GAME_PHYSICS_SIMULATION );
- }
-#if !defined( CLIENT_DLL )
- SimThink_EntityChanged( this );
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Sets/Gets the next think based on context index
-//-----------------------------------------------------------------------------
-void CBaseEntity::SetNextThink( int nContextIndex, float thinkTime )
-{
- int thinkTick = ( thinkTime == TICK_NEVER_THINK ) ? TICK_NEVER_THINK : TIME_TO_TICKS( thinkTime );
-
- if (nContextIndex < 0)
- {
- SetNextThink( thinkTime );
- }
- else
- {
- m_aThinkFunctions[nContextIndex].m_nNextThinkTick = thinkTick;
- }
- CheckHasThinkFunction( thinkTick == TICK_NEVER_THINK ? false : true );
-}
-
-void CBaseEntity::SetLastThink( int nContextIndex, float thinkTime )
-{
- int thinkTick = ( thinkTime == TICK_NEVER_THINK ) ? TICK_NEVER_THINK : TIME_TO_TICKS( thinkTime );
-
- if (nContextIndex < 0)
- {
- m_nLastThinkTick = thinkTick;
- }
- else
- {
- m_aThinkFunctions[nContextIndex].m_nLastThinkTick = thinkTick;
- }
-}
-
-float CBaseEntity::GetNextThink( int nContextIndex ) const
-{
- if (nContextIndex < 0)
- return m_nNextThinkTick * TICK_INTERVAL;
-
- return m_aThinkFunctions[nContextIndex].m_nNextThinkTick * TICK_INTERVAL;
-}
-
-int CBaseEntity::GetNextThinkTick( int nContextIndex ) const
-{
- if (nContextIndex < 0)
- return m_nNextThinkTick;
-
- return m_aThinkFunctions[nContextIndex].m_nNextThinkTick;
-}
-
-
-int CheckEntityVelocity( Vector &v )
-{
- float r = k_flMaxEntitySpeed;
- if (
- v.x > -r && v.x < r &&
- v.y > -r && v.y < r &&
- v.z > -r && v.z < r)
- {
- // The usual case. It's totally reasonable
- return 1;
- }
- float speed = v.Length();
- if ( speed < k_flMaxEntitySpeed * 100.0f )
- {
- // Sort of suspicious. Clamp it
- v *= k_flMaxEntitySpeed / speed;
- return 0;
- }
-
- // A terrible, horrible, no good, very bad velocity.
- return -1;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: My physics object has been updated, react or extract data
-//-----------------------------------------------------------------------------
-void CBaseEntity::VPhysicsUpdate( IPhysicsObject *pPhysics )
-{
- switch( GetMoveType() )
- {
- case MOVETYPE_VPHYSICS:
- {
- if ( GetMoveParent() )
- {
- DevWarning("Updating physics on object in hierarchy %s!\n", GetClassname());
- return;
- }
- Vector origin;
- QAngle angles;
-
- pPhysics->GetPosition( &origin, &angles );
-
- if ( !IsEntityQAngleReasonable( angles ) )
- {
- if ( CheckEmitReasonablePhysicsSpew() )
- {
- Warning( "Ignoring bogus angles (%f,%f,%f) from vphysics! (entity %s)\n", angles.x, angles.y, angles.z, GetDebugName() );
- }
- angles = vec3_angle;
- }
-#ifndef CLIENT_DLL
- Vector prevOrigin = GetAbsOrigin();
-#endif
-
- if ( IsEntityPositionReasonable( origin ) )
- {
- SetAbsOrigin( origin );
- }
- else
- {
- if ( CheckEmitReasonablePhysicsSpew() )
- {
- Warning( "Ignoring unreasonable position (%f,%f,%f) from vphysics! (entity %s)\n", origin.x, origin.y, origin.z, GetDebugName() );
- }
- }
-
- for ( int i = 0; i < 3; ++i )
- {
- angles[ i ] = AngleNormalize( angles[ i ] );
- }
- SetAbsAngles( angles );
-
- // Interactive debris converts back to debris when it comes to rest
- if ( pPhysics->IsAsleep() && GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
- {
- SetCollisionGroup( COLLISION_GROUP_DEBRIS );
- }
-
-#ifndef CLIENT_DLL
- PhysicsTouchTriggers( &prevOrigin );
- PhysicsRelinkChildren(gpGlobals->frametime);
-#endif
- }
- break;
-
- case MOVETYPE_STEP:
- break;
-
- case MOVETYPE_PUSH:
-#ifndef CLIENT_DLL
- VPhysicsUpdatePusher( pPhysics );
-#endif
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Init this object's physics as a static
-//-----------------------------------------------------------------------------
-IPhysicsObject *CBaseEntity::VPhysicsInitStatic( void )
-{
- if ( !VPhysicsInitSetup() )
- return NULL;
-
-#ifndef CLIENT_DLL
- // If this entity has a move parent, it needs to be shadow, not static
- if ( GetMoveParent() )
- {
- // must be SOLID_VPHYSICS if in hierarchy to solve collisions correctly
- if ( GetSolid() == SOLID_BSP && GetRootMoveParent()->GetSolid() != SOLID_BSP )
- {
- SetSolid( SOLID_VPHYSICS );
- }
-
- return VPhysicsInitShadow( false, false );
- }
-#endif
-
- // No physics
- if ( GetSolid() == SOLID_NONE )
- return NULL;
-
- // create a static physics objct
- IPhysicsObject *pPhysicsObject = NULL;
- if ( GetSolid() == SOLID_BBOX )
- {
- pPhysicsObject = PhysModelCreateBox( this, WorldAlignMins(), WorldAlignMaxs(), GetAbsOrigin(), true );
- }
- else
- {
- pPhysicsObject = PhysModelCreateUnmoveable( this, GetModelIndex(), GetAbsOrigin(), GetAbsAngles() );
- }
- VPhysicsSetObject( pPhysicsObject );
- return pPhysicsObject;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pPhysics -
-//-----------------------------------------------------------------------------
-void CBaseEntity::VPhysicsSetObject( IPhysicsObject *pPhysics )
-{
- if ( m_pPhysicsObject && pPhysics )
- {
- Warning( "Overwriting physics object for %s\n", GetClassname() );
- }
- m_pPhysicsObject = pPhysics;
- if ( pPhysics && !m_pPhysicsObject )
- {
- CollisionRulesChanged();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBaseEntity::VPhysicsDestroyObject( void )
-{
- if ( m_pPhysicsObject )
- {
-#ifndef CLIENT_DLL
- PhysRemoveShadow( this );
-#endif
- PhysDestroyObject( m_pPhysicsObject, this );
- m_pPhysicsObject = NULL;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool CBaseEntity::VPhysicsInitSetup()
-{
-#ifndef CLIENT_DLL
- // don't support logical ents
- if ( !edict() || IsMarkedForDeletion() )
- return false;
-#endif
-
- // If this entity already has a physics object, then it should have been deleted prior to making this call.
- Assert(!m_pPhysicsObject);
- VPhysicsDestroyObject();
-
- // make sure absorigin / absangles are correct
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: This creates a normal vphysics simulated object
-// physics alone determines where it goes (gravity, friction, etc)
-// and the entity receives updates from vphysics. SetAbsOrigin(), etc do not affect the object!
-//-----------------------------------------------------------------------------
-IPhysicsObject *CBaseEntity::VPhysicsInitNormal( SolidType_t solidType, int nSolidFlags, bool createAsleep, solid_t *pSolid )
-{
- if ( !VPhysicsInitSetup() )
- return NULL;
-
- // NOTE: This has to occur before PhysModelCreate because that call will
- // call back into ShouldCollide(), which uses solidtype for rules.
- SetSolid( solidType );
- SetSolidFlags( nSolidFlags );
-
- // No physics
- if ( solidType == SOLID_NONE )
- {
- return NULL;
- }
-
- // create a normal physics object
- IPhysicsObject *pPhysicsObject = PhysModelCreate( this, GetModelIndex(), GetAbsOrigin(), GetAbsAngles(), pSolid );
- if ( pPhysicsObject )
- {
- VPhysicsSetObject( pPhysicsObject );
- SetMoveType( MOVETYPE_VPHYSICS );
-
- if ( !createAsleep )
- {
- pPhysicsObject->Wake();
- }
- }
-
- return pPhysicsObject;
-}
-
-// This creates a vphysics object with a shadow controller that follows the AI
-IPhysicsObject *CBaseEntity::VPhysicsInitShadow( bool allowPhysicsMovement, bool allowPhysicsRotation, solid_t *pSolid )
-{
- if ( !VPhysicsInitSetup() )
- return NULL;
-
- // No physics
- if ( GetSolid() == SOLID_NONE )
- return NULL;
-
- const Vector &origin = GetAbsOrigin();
- QAngle angles = GetAbsAngles();
- IPhysicsObject *pPhysicsObject = NULL;
-
- if ( GetSolid() == SOLID_BBOX )
- {
- // adjust these so the game tracing epsilons match the physics minimum separation distance
- // this will shrink the vphysics version of the model by the difference in epsilons
- float radius = 0.25f - DIST_EPSILON;
- Vector mins = WorldAlignMins() + Vector(radius, radius, radius);
- Vector maxs = WorldAlignMaxs() - Vector(radius, radius, radius);
- pPhysicsObject = PhysModelCreateBox( this, mins, maxs, origin, false );
- angles = vec3_angle;
- }
- else if ( GetSolid() == SOLID_OBB )
- {
- pPhysicsObject = PhysModelCreateOBB( this, CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), origin, angles, false );
- }
- else
- {
- pPhysicsObject = PhysModelCreate( this, GetModelIndex(), origin, angles, pSolid );
- }
- if ( !pPhysicsObject )
- return NULL;
-
- VPhysicsSetObject( pPhysicsObject );
- // UNDONE: Tune these speeds!!!
- pPhysicsObject->SetShadow( 1e4, 1e4, allowPhysicsMovement, allowPhysicsRotation );
- pPhysicsObject->UpdateShadow( origin, angles, false, 0 );
- return pPhysicsObject;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool CBaseEntity::CreateVPhysics()
-{
- return false;
-}
-
-bool CBaseEntity::IsStandable() const
-{
- if (GetSolidFlags() & FSOLID_NOT_STANDABLE)
- return false;
-
- if ( GetSolid() == SOLID_BSP || GetSolid() == SOLID_VPHYSICS || GetSolid() == SOLID_BBOX )
- return true;
-
- return IsBSPModel( );
-}
-
-bool CBaseEntity::IsBSPModel() const
-{
- if ( GetSolid() == SOLID_BSP )
- return true;
-
- const model_t *model = modelinfo->GetModel( GetModelIndex() );
-
- if ( GetSolid() == SOLID_VPHYSICS && modelinfo->GetModelType( model ) == mod_brush )
- return true;
-
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Invalidates the abs state of all children
-//-----------------------------------------------------------------------------
-void CBaseEntity::InvalidatePhysicsRecursive( int nChangeFlags )
-{
- // Main entry point for dirty flag setting for the 90% case
- // 1) If the origin changes, then we have to update abstransform, Shadow projection, PVS, KD-tree,
- // client-leaf system.
- // 2) If the angles change, then we have to update abstransform, Shadow projection,
- // shadow render-to-texture, client-leaf system, and surrounding bounds.
- // Children have to additionally update absvelocity, KD-tree, and PVS.
- // If the surrounding bounds actually update, when we also need to update the KD-tree and the PVS.
- // 3) If it's due to attachment, then all children who are attached to an attachment point
- // are assumed to have dirty origin + angles.
-
- // Other stuff:
- // 1) Marking the surrounding bounds dirty will automatically mark KD tree + PVS dirty.
-
- int nDirtyFlags = 0;
-
- if ( nChangeFlags & VELOCITY_CHANGED )
- {
- nDirtyFlags |= EFL_DIRTY_ABSVELOCITY;
- }
-
- if ( nChangeFlags & POSITION_CHANGED )
- {
- nDirtyFlags |= EFL_DIRTY_ABSTRANSFORM;
-
-#ifndef CLIENT_DLL
- NetworkProp()->MarkPVSInformationDirty();
-#endif
-
- // NOTE: This will also mark shadow projection + client leaf dirty
- CollisionProp()->MarkPartitionHandleDirty();
- }
-
- // NOTE: This has to be done after velocity + position are changed
- // because we change the nChangeFlags for the child entities
- if ( nChangeFlags & ANGLES_CHANGED )
- {
- nDirtyFlags |= EFL_DIRTY_ABSTRANSFORM;
- if ( CollisionProp()->DoesRotationInvalidateSurroundingBox() )
- {
- // NOTE: This will handle the KD-tree, surrounding bounds, PVS
- // render-to-texture shadow, shadow projection, and client leaf dirty
- CollisionProp()->MarkSurroundingBoundsDirty();
- }
- else
- {
-#ifdef CLIENT_DLL
- MarkRenderHandleDirty();
- g_pClientShadowMgr->AddToDirtyShadowList( this );
- g_pClientShadowMgr->MarkRenderToTextureShadowDirty( GetShadowHandle() );
-#endif
- }
-
- // This is going to be used for all children: children
- // have position + velocity changed
- nChangeFlags |= POSITION_CHANGED | VELOCITY_CHANGED;
- }
-
- AddEFlags( nDirtyFlags );
-
- // Set flags for children
- bool bOnlyDueToAttachment = false;
- if ( nChangeFlags & ANIMATION_CHANGED )
- {
-#ifdef CLIENT_DLL
- g_pClientShadowMgr->MarkRenderToTextureShadowDirty( GetShadowHandle() );
-#endif
-
- // Only set this flag if the only thing that changed us was the animation.
- // If position or something else changed us, then we must tell all children.
- if ( !( nChangeFlags & (POSITION_CHANGED | VELOCITY_CHANGED | ANGLES_CHANGED) ) )
- {
- bOnlyDueToAttachment = true;
- }
-
- nChangeFlags = POSITION_CHANGED | ANGLES_CHANGED | VELOCITY_CHANGED;
- }
-
- for (CBaseEntity *pChild = FirstMoveChild(); pChild; pChild = pChild->NextMovePeer())
- {
- // If this is due to the parent animating, only invalidate children that are parented to an attachment
- // Entities that are following also access attachments points on parents and must be invalidated.
- if ( bOnlyDueToAttachment )
- {
-#ifdef CLIENT_DLL
- if ( (pChild->GetParentAttachment() == 0) && !pChild->IsFollowingEntity() )
- continue;
-#else
- if ( pChild->GetParentAttachment() == 0 )
- continue;
-#endif
- }
- pChild->InvalidatePhysicsRecursive( nChangeFlags );
- }
-
- //
- // This code should really be in here, or the bone cache should not be in world space.
- // Since the bone transforms are in world space, if we move or rotate the entity, its
- // bones should be marked invalid.
- //
- // As it is, we're near ship, and don't have time to setup a good A/B test of how much
- // overhead this fix would add. We've also only got one known case where the lack of
- // this fix is screwing us, and I just fixed it, so I'm leaving this commented out for now.
- //
- // Hopefully, we'll put the bone cache in entity space and remove the need for this fix.
- //
- //#ifdef CLIENT_DLL
- // if ( nChangeFlags & (POSITION_CHANGED | ANGLES_CHANGED | ANIMATION_CHANGED) )
- // {
- // C_BaseAnimating *pAnim = GetBaseAnimating();
- // if ( pAnim )
- // pAnim->InvalidateBoneCache();
- // }
- //#endif
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Returns the highest parent of an entity
-//-----------------------------------------------------------------------------
-CBaseEntity *CBaseEntity::GetRootMoveParent()
-{
- CBaseEntity *pEntity = this;
- CBaseEntity *pParent = this->GetMoveParent();
- while ( pParent )
- {
- pEntity = pParent;
- pParent = pEntity->GetMoveParent();
- }
-
- return pEntity;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: static method
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseEntity::IsPrecacheAllowed()
-{
- return m_bAllowPrecache;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: static method
-// Input : allow -
-//-----------------------------------------------------------------------------
-void CBaseEntity::SetAllowPrecache( bool allow )
-{
- m_bAllowPrecache = allow;
-}
-
-/*
-================
-FireBullets
-
-Go to the trouble of combining multiple pellets into a single damage call.
-================
-*/
-
-#if defined( GAME_DLL )
-class CBulletsTraceFilter : public CTraceFilterSimpleList
-{
-public:
- CBulletsTraceFilter( int collisionGroup ) : CTraceFilterSimpleList( collisionGroup ) {}
-
- bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
- {
- if ( m_PassEntities.Count() )
- {
- CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
- CBaseEntity *pPassEntity = EntityFromEntityHandle( m_PassEntities[0] );
- if ( pEntity && pPassEntity && pEntity->GetOwnerEntity() == pPassEntity &&
- pPassEntity->IsSolidFlagSet(FSOLID_NOT_SOLID) && pPassEntity->IsSolidFlagSet( FSOLID_CUSTOMBOXTEST ) &&
- pPassEntity->IsSolidFlagSet( FSOLID_CUSTOMRAYTEST ) )
- {
- // It's a bone follower of the entity to ignore (toml 8/3/2007)
- return false;
- }
- }
- return CTraceFilterSimpleList::ShouldHitEntity( pHandleEntity, contentsMask );
- }
-
-};
-#else
-typedef CTraceFilterSimpleList CBulletsTraceFilter;
-#endif
-
-void CBaseEntity::FireBullets( const FireBulletsInfo_t &info )
-{
- static int tracerCount;
- trace_t tr;
- CAmmoDef* pAmmoDef = GetAmmoDef();
- int nDamageType = pAmmoDef->DamageType(info.m_iAmmoType);
- int nAmmoFlags = pAmmoDef->Flags(info.m_iAmmoType);
-
- bool bDoServerEffects = true;
-
-#if defined( HL2MP ) && defined( GAME_DLL )
- bDoServerEffects = false;
-#endif
-
-#if defined( GAME_DLL )
- if( IsPlayer() )
- {
- CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>(this);
-
- int rumbleEffect = pPlayer->GetActiveWeapon()->GetRumbleEffect();
-
- if( rumbleEffect != RUMBLE_INVALID )
- {
- if( rumbleEffect == RUMBLE_SHOTGUN_SINGLE )
- {
- if( info.m_iShots == 12 )
- {
- // Upgrade to double barrel rumble effect
- rumbleEffect = RUMBLE_SHOTGUN_DOUBLE;
- }
- }
-
- pPlayer->RumbleEffect( rumbleEffect, 0, RUMBLE_FLAG_RESTART );
- }
- }
-#endif// GAME_DLL
-
- int iPlayerDamage = info.m_iPlayerDamage;
- if ( iPlayerDamage == 0 )
- {
- if ( nAmmoFlags & AMMO_INTERPRET_PLRDAMAGE_AS_DAMAGE_TO_PLAYER )
- {
- iPlayerDamage = pAmmoDef->PlrDamage( info.m_iAmmoType );
- }
- }
-
- // the default attacker is ourselves
- CBaseEntity *pAttacker = info.m_pAttacker ? info.m_pAttacker : this;
-
- // Make sure we don't have a dangling damage target from a recursive call
- if ( g_MultiDamage.GetTarget() != NULL )
- {
- ApplyMultiDamage();
- }
-
- ClearMultiDamage();
- g_MultiDamage.SetDamageType( nDamageType | DMG_NEVERGIB );
-
- Vector vecDir;
- Vector vecEnd;
-
- // Skip multiple entities when tracing
- CBulletsTraceFilter traceFilter( COLLISION_GROUP_NONE );
- traceFilter.SetPassEntity( this ); // Standard pass entity for THIS so that it can be easily removed from the list after passing through a portal
- traceFilter.AddEntityToIgnore( info.m_pAdditionalIgnoreEnt );
-
-#if defined( HL2_EPISODIC ) && defined( GAME_DLL )
- // FIXME: We need to emulate this same behavior on the client as well -- jdw
- // Also ignore a vehicle we're a passenger in
- if ( MyCombatCharacterPointer() != NULL && MyCombatCharacterPointer()->IsInAVehicle() )
- {
- traceFilter.AddEntityToIgnore( MyCombatCharacterPointer()->GetVehicleEntity() );
- }
-#endif // SERVER_DLL
-
- bool bUnderwaterBullets = ShouldDrawUnderwaterBulletBubbles();
- bool bStartedInWater = false;
- if ( bUnderwaterBullets )
- {
- bStartedInWater = ( enginetrace->GetPointContents( info.m_vecSrc ) & (CONTENTS_WATER|CONTENTS_SLIME) ) != 0;
- }
-
- // Prediction is only usable on players
- int iSeed = 0;
- if ( IsPlayer() )
- {
- iSeed = CBaseEntity::GetPredictionRandomSeed() & 255;
- }
-
-#if defined( HL2MP ) && defined( GAME_DLL )
- int iEffectSeed = iSeed;
-#endif
- //-----------------------------------------------------
- // Set up our shot manipulator.
- //-----------------------------------------------------
- CShotManipulator Manipulator( info.m_vecDirShooting );
-
- bool bDoImpacts = false;
- bool bDoTracers = false;
-
- float flCumulativeDamage = 0.0f;
-
- for (int iShot = 0; iShot < info.m_iShots; iShot++)
- {
- bool bHitWater = false;
- bool bHitGlass = false;
-
- // Prediction is only usable on players
- if ( IsPlayer() )
- {
- RandomSeed( iSeed ); // init random system with this seed
- }
-
- // If we're firing multiple shots, and the first shot has to be bang on target, ignore spread
- if ( iShot == 0 && info.m_iShots > 1 && (info.m_nFlags & FIRE_BULLETS_FIRST_SHOT_ACCURATE) )
- {
- vecDir = Manipulator.GetShotDirection();
- }
- else
- {
-
- // Don't run the biasing code for the player at the moment.
- vecDir = Manipulator.ApplySpread( info.m_vecSpread );
- }
-
- vecEnd = info.m_vecSrc + vecDir * info.m_flDistance;
-
-#ifdef PORTAL
- CProp_Portal *pShootThroughPortal = NULL;
- float fPortalFraction = 2.0f;
-#endif
-
-
- if( IsPlayer() && info.m_iShots > 1 && iShot % 2 )
- {
- // Half of the shotgun pellets are hulls that make it easier to hit targets with the shotgun.
-#ifdef PORTAL
- Ray_t rayBullet;
- rayBullet.Init( info.m_vecSrc, vecEnd );
- pShootThroughPortal = UTIL_Portal_FirstAlongRay( rayBullet, fPortalFraction );
- if ( !UTIL_Portal_TraceRay_Bullets( pShootThroughPortal, rayBullet, MASK_SHOT, &traceFilter, &tr ) )
- {
- pShootThroughPortal = NULL;
- }
-#else
- AI_TraceHull( info.m_vecSrc, vecEnd, Vector( -3, -3, -3 ), Vector( 3, 3, 3 ), MASK_SHOT, &traceFilter, &tr );
-#endif //#ifdef PORTAL
- }
- else
- {
-#ifdef PORTAL
- Ray_t rayBullet;
- rayBullet.Init( info.m_vecSrc, vecEnd );
- pShootThroughPortal = UTIL_Portal_FirstAlongRay( rayBullet, fPortalFraction );
- if ( !UTIL_Portal_TraceRay_Bullets( pShootThroughPortal, rayBullet, MASK_SHOT, &traceFilter, &tr ) )
- {
- pShootThroughPortal = NULL;
- }
-#elif TF_DLL
- CTraceFilterIgnoreFriendlyCombatItems traceFilterCombatItem( this, COLLISION_GROUP_NONE, GetTeamNumber() );
- if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
- {
- CTraceFilterChain traceFilterChain( &traceFilter, &traceFilterCombatItem );
- AI_TraceLine(info.m_vecSrc, vecEnd, MASK_SHOT, &traceFilterChain, &tr);
- }
- else
- {
- AI_TraceLine(info.m_vecSrc, vecEnd, MASK_SHOT, &traceFilter, &tr);
- }
-#else
- AI_TraceLine(info.m_vecSrc, vecEnd, MASK_SHOT, &traceFilter, &tr);
-#endif //#ifdef PORTAL
- }
-
- // Tracker 70354/63250: ywb 8/2/07
- // Fixes bug where trace from turret with attachment point outside of Vcollide
- // starts solid so doesn't hit anything else in the world and the final coord
- // is outside of the MAX_COORD_FLOAT range. This cause trying to send the end pos
- // of the tracer down to the client with an origin which is out-of-range for networking
- if ( tr.startsolid )
- {
- tr.endpos = tr.startpos;
- tr.fraction = 0.0f;
- }
-
- // bullet's final direction can be changed by passing through a portal
-#ifdef PORTAL
- if ( !tr.startsolid )
- {
- vecDir = tr.endpos - tr.startpos;
- VectorNormalize( vecDir );
- }
-#endif
-
-#ifdef GAME_DLL
- if ( ai_debug_shoot_positions.GetBool() )
- NDebugOverlay::Line(info.m_vecSrc, vecEnd, 255, 255, 255, false, .1 );
-#endif
-
- if ( bStartedInWater )
- {
-#ifdef GAME_DLL
- Vector vBubbleStart = info.m_vecSrc;
- Vector vBubbleEnd = tr.endpos;
-
-#ifdef PORTAL
- if ( pShootThroughPortal )
- {
- vBubbleEnd = info.m_vecSrc + ( vecEnd - info.m_vecSrc ) * fPortalFraction;
- }
-#endif //#ifdef PORTAL
-
- CreateBubbleTrailTracer( vBubbleStart, vBubbleEnd, vecDir );
-
-#ifdef PORTAL
- if ( pShootThroughPortal )
- {
- Vector vTransformedIntersection;
- UTIL_Portal_PointTransform( pShootThroughPortal->MatrixThisToLinked(), vBubbleEnd, vTransformedIntersection );
-
- CreateBubbleTrailTracer( vTransformedIntersection, tr.endpos, vecDir );
- }
-#endif //#ifdef PORTAL
-
-#endif //#ifdef GAME_DLL
- bHitWater = true;
- }
-
- // Now hit all triggers along the ray that respond to shots...
- // Clip the ray to the first collided solid returned from traceline
- CTakeDamageInfo triggerInfo( pAttacker, pAttacker, info.m_flDamage, nDamageType );
- CalculateBulletDamageForce( &triggerInfo, info.m_iAmmoType, vecDir, tr.endpos );
- triggerInfo.ScaleDamageForce( info.m_flDamageForceScale );
- triggerInfo.SetAmmoType( info.m_iAmmoType );
-#ifdef GAME_DLL
- TraceAttackToTriggers( triggerInfo, tr.startpos, tr.endpos, vecDir );
-#endif
-
- // Make sure given a valid bullet type
- if (info.m_iAmmoType == -1)
- {
- DevMsg("ERROR: Undefined ammo type!\n");
- return;
- }
-
- Vector vecTracerDest = tr.endpos;
-
- // do damage, paint decals
- if (tr.fraction != 1.0)
- {
-#ifdef GAME_DLL
- UpdateShotStatistics( tr );
-
- // For shots that don't need persistance
- int soundEntChannel = ( info.m_nFlags&FIRE_BULLETS_TEMPORARY_DANGER_SOUND ) ? SOUNDENT_CHANNEL_BULLET_IMPACT : SOUNDENT_CHANNEL_UNSPECIFIED;
-
- CSoundEnt::InsertSound( SOUND_BULLET_IMPACT, tr.endpos, 200, 0.5, this, soundEntChannel );
-#endif
-
- // See if the bullet ended up underwater + started out of the water
- if ( !bHitWater && ( enginetrace->GetPointContents( tr.endpos ) & (CONTENTS_WATER|CONTENTS_SLIME) ) )
- {
- bHitWater = HandleShotImpactingWater( info, vecEnd, &traceFilter, &vecTracerDest );
- }
-
- float flActualDamage = info.m_flDamage;
-
- // If we hit a player, and we have player damage specified, use that instead
- // Adrian: Make sure to use the currect value if we hit a vehicle the player is currently driving.
- if ( iPlayerDamage )
- {
- if ( tr.m_pEnt->IsPlayer() )
- {
- flActualDamage = iPlayerDamage;
- }
-#ifdef GAME_DLL
- else if ( tr.m_pEnt->GetServerVehicle() )
- {
- if ( tr.m_pEnt->GetServerVehicle()->GetPassenger() && tr.m_pEnt->GetServerVehicle()->GetPassenger()->IsPlayer() )
- {
- flActualDamage = iPlayerDamage;
- }
- }
-#endif
- }
-
- int nActualDamageType = nDamageType;
- if ( flActualDamage == 0.0 )
- {
- flActualDamage = g_pGameRules->GetAmmoDamage( pAttacker, tr.m_pEnt, info.m_iAmmoType );
- }
- else
- {
- nActualDamageType = nDamageType | ((flActualDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB );
- }
-
- if ( !bHitWater || ((info.m_nFlags & FIRE_BULLETS_DONT_HIT_UNDERWATER) == 0) )
- {
- // Damage specified by function parameter
- CTakeDamageInfo dmgInfo( this, pAttacker, flActualDamage, nActualDamageType );
- ModifyFireBulletsDamage( &dmgInfo );
- CalculateBulletDamageForce( &dmgInfo, info.m_iAmmoType, vecDir, tr.endpos );
- dmgInfo.ScaleDamageForce( info.m_flDamageForceScale );
- dmgInfo.SetAmmoType( info.m_iAmmoType );
- tr.m_pEnt->DispatchTraceAttack( dmgInfo, vecDir, &tr );
-
- if ( ToBaseCombatCharacter( tr.m_pEnt ) )
- {
- flCumulativeDamage += dmgInfo.GetDamage();
- }
-
- if ( bStartedInWater || !bHitWater || (info.m_nFlags & FIRE_BULLETS_ALLOW_WATER_SURFACE_IMPACTS) )
- {
- if ( bDoServerEffects == true )
- {
- DoImpactEffect( tr, nDamageType );
- }
- else
- {
- bDoImpacts = true;
- }
- }
- else
- {
- // We may not impact, but we DO need to affect ragdolls on the client
- CEffectData data;
- data.m_vStart = tr.startpos;
- data.m_vOrigin = tr.endpos;
- data.m_nDamageType = nDamageType;
-
- DispatchEffect( "RagdollImpact", data );
- }
-
-#ifdef GAME_DLL
- if ( nAmmoFlags & AMMO_FORCE_DROP_IF_CARRIED )
- {
- // Make sure if the player is holding this, he drops it
- Pickup_ForcePlayerToDropThisObject( tr.m_pEnt );
- }
-#endif
- }
- }
-
- // See if we hit glass
- if ( tr.m_pEnt != NULL )
- {
-#ifdef GAME_DLL
- surfacedata_t *psurf = physprops->GetSurfaceData( tr.surface.surfaceProps );
- if ( ( psurf != NULL ) && ( psurf->game.material == CHAR_TEX_GLASS ) && ( tr.m_pEnt->ClassMatches( "func_breakable" ) ) )
- {
- // Query the func_breakable for whether it wants to allow for bullet penetration
- if ( tr.m_pEnt->HasSpawnFlags( SF_BREAK_NO_BULLET_PENETRATION ) == false )
- {
- bHitGlass = true;
- }
- }
-#endif
- }
-
- if ( ( info.m_iTracerFreq != 0 ) && ( tracerCount++ % info.m_iTracerFreq ) == 0 && ( bHitGlass == false ) )
- {
- if ( bDoServerEffects == true )
- {
- Vector vecTracerSrc = vec3_origin;
- ComputeTracerStartPosition( info.m_vecSrc, &vecTracerSrc );
-
- trace_t Tracer;
- Tracer = tr;
- Tracer.endpos = vecTracerDest;
-
-#ifdef PORTAL
- if ( pShootThroughPortal )
- {
- Tracer.endpos = info.m_vecSrc + ( vecEnd - info.m_vecSrc ) * fPortalFraction;
- }
-#endif //#ifdef PORTAL
-
- MakeTracer( vecTracerSrc, Tracer, pAmmoDef->TracerType(info.m_iAmmoType) );
-
-#ifdef PORTAL
- if ( pShootThroughPortal )
- {
- Vector vTransformedIntersection;
- UTIL_Portal_PointTransform( pShootThroughPortal->MatrixThisToLinked(), Tracer.endpos, vTransformedIntersection );
- ComputeTracerStartPosition( vTransformedIntersection, &vecTracerSrc );
-
- Tracer.endpos = vecTracerDest;
-
- MakeTracer( vecTracerSrc, Tracer, pAmmoDef->TracerType(info.m_iAmmoType) );
-
- // Shooting through a portal, the damage direction is translated through the passed-through portal
- // so the damage indicator hud animation is correct
- Vector vDmgOriginThroughPortal;
- UTIL_Portal_PointTransform( pShootThroughPortal->MatrixThisToLinked(), info.m_vecSrc, vDmgOriginThroughPortal );
- g_MultiDamage.SetDamagePosition ( vDmgOriginThroughPortal );
- }
- else
- {
- g_MultiDamage.SetDamagePosition ( info.m_vecSrc );
- }
-#endif //#ifdef PORTAL
- }
- else
- {
- bDoTracers = true;
- }
- }
-
- //NOTENOTE: We could expand this to a more general solution for various material penetration types (wood, thin metal, etc)
-
- // See if we should pass through glass
-#ifdef GAME_DLL
- if ( bHitGlass )
- {
- HandleShotImpactingGlass( info, tr, vecDir, &traceFilter );
- }
-#endif
-
- iSeed++;
- }
-
-#if defined( HL2MP ) && defined( GAME_DLL )
- if ( bDoServerEffects == false )
- {
- TE_HL2MPFireBullets( entindex(), tr.startpos, info.m_vecDirShooting, info.m_iAmmoType, iEffectSeed, info.m_iShots, info.m_vecSpread.x, bDoTracers, bDoImpacts );
- }
-#endif
-
-#ifdef GAME_DLL
- ApplyMultiDamage();
-
- if ( IsPlayer() && flCumulativeDamage > 0.0f )
- {
- CBasePlayer *pPlayer = static_cast< CBasePlayer * >( this );
- CTakeDamageInfo dmgInfo( this, pAttacker, flCumulativeDamage, nDamageType );
- gamestats->Event_WeaponHit( pPlayer, info.m_bPrimaryAttack, pPlayer->GetActiveWeapon()->GetClassname(), dmgInfo );
- }
-#endif
-}
-
-
-//-----------------------------------------------------------------------------
-// Should we draw bubbles underwater?
-//-----------------------------------------------------------------------------
-bool CBaseEntity::ShouldDrawUnderwaterBulletBubbles()
-{
-#if defined( HL2_DLL ) && defined( GAME_DLL )
- CBaseEntity *pPlayer = ( gpGlobals->maxClients == 1 ) ? UTIL_GetLocalPlayer() : NULL;
- return pPlayer && (pPlayer->GetWaterLevel() == 3);
-#else
- return false;
-#endif
-}
-
-
-//-----------------------------------------------------------------------------
-// Handle shot entering water
-//-----------------------------------------------------------------------------
-bool CBaseEntity::HandleShotImpactingWater( const FireBulletsInfo_t &info,
- const Vector &vecEnd, ITraceFilter *pTraceFilter, Vector *pVecTracerDest )
-{
- trace_t waterTrace;
-
- // Trace again with water enabled
- AI_TraceLine( info.m_vecSrc, vecEnd, (MASK_SHOT|CONTENTS_WATER|CONTENTS_SLIME), pTraceFilter, &waterTrace );
-
- // See if this is the point we entered
- if ( ( enginetrace->GetPointContents( waterTrace.endpos - Vector(0,0,0.1f) ) & (CONTENTS_WATER|CONTENTS_SLIME) ) == 0 )
- return false;
-
- if ( ShouldDrawWaterImpacts() )
- {
- int nMinSplashSize = GetAmmoDef()->MinSplashSize(info.m_iAmmoType);
- int nMaxSplashSize = GetAmmoDef()->MaxSplashSize(info.m_iAmmoType);
-
- CEffectData data;
- data.m_vOrigin = waterTrace.endpos;
- data.m_vNormal = waterTrace.plane.normal;
- data.m_flScale = random->RandomFloat( nMinSplashSize, nMaxSplashSize );
- if ( waterTrace.contents & CONTENTS_SLIME )
- {
- data.m_fFlags |= FX_WATER_IN_SLIME;
- }
- DispatchEffect( "gunshotsplash", data );
- }
-
-#ifdef GAME_DLL
- if ( ShouldDrawUnderwaterBulletBubbles() )
- {
- CWaterBullet *pWaterBullet = ( CWaterBullet * )CreateEntityByName( "waterbullet" );
- if ( pWaterBullet )
- {
- pWaterBullet->Spawn( waterTrace.endpos, info.m_vecDirShooting );
-
- CEffectData tracerData;
- tracerData.m_vStart = waterTrace.endpos;
- tracerData.m_vOrigin = waterTrace.endpos + info.m_vecDirShooting * 400.0f;
- tracerData.m_fFlags = TRACER_TYPE_WATERBULLET;
- DispatchEffect( "TracerSound", tracerData );
- }
- }
-#endif
-
- *pVecTracerDest = waterTrace.endpos;
- return true;
-}
-
-
-ITraceFilter* CBaseEntity::GetBeamTraceFilter( void )
-{
- return NULL;
-}
-
-void CBaseEntity::DispatchTraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
-{
-#ifdef GAME_DLL
- // Make sure our damage filter allows the damage.
- if ( !PassesDamageFilter( info ))
- {
- return;
- }
-#endif
-
- TraceAttack( info, vecDir, ptr, pAccumulator );
-}
-
-void CBaseEntity::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
-{
- Vector vecOrigin = ptr->endpos - vecDir * 4;
-
- if ( m_takedamage )
- {
-#ifdef GAME_DLL
- if ( pAccumulator )
- {
- pAccumulator->AccumulateMultiDamage( info, this );
- }
- else
-#endif // GAME_DLL
- {
- AddMultiDamage( info, this );
- }
-
- int blood = BloodColor();
-
- if ( blood != DONT_BLEED )
- {
- SpawnBlood( vecOrigin, vecDir, blood, info.GetDamage() );// a little surface blood.
- TraceBleed( info.GetDamage(), vecDir, ptr, info.GetDamageType() );
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Allows the shooter to change the impact effect of his bullets
-//-----------------------------------------------------------------------------
-void CBaseEntity::DoImpactEffect( trace_t &tr, int nDamageType )
-{
- // give shooter a chance to do a custom impact.
- UTIL_ImpactTrace( &tr, nDamageType );
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes the tracer start position
-//-----------------------------------------------------------------------------
-void CBaseEntity::ComputeTracerStartPosition( const Vector &vecShotSrc, Vector *pVecTracerStart )
-{
-#ifndef HL2MP
- if ( g_pGameRules->IsMultiplayer() )
- {
- // NOTE: we do this because in MakeTracer, we force it to use the attachment position
- // in multiplayer, so the results from this function should never actually get used.
- pVecTracerStart->Init( 999, 999, 999 );
- return;
- }
-#endif
-
- if ( IsPlayer() )
- {
- // adjust tracer position for player
- Vector forward, right;
- CBasePlayer *pPlayer = ToBasePlayer( this );
- pPlayer->EyeVectors( &forward, &right, NULL );
- *pVecTracerStart = vecShotSrc + Vector ( 0 , 0 , -4 ) + right * 2 + forward * 16;
- }
- else
- {
- *pVecTracerStart = vecShotSrc;
-
- CBaseCombatCharacter *pBCC = MyCombatCharacterPointer();
- if ( pBCC != NULL )
- {
- CBaseCombatWeapon *pWeapon = pBCC->GetActiveWeapon();
-
- if ( pWeapon != NULL )
- {
- Vector vecMuzzle;
- QAngle vecMuzzleAngles;
-
- if ( pWeapon->GetAttachment( 1, vecMuzzle, vecMuzzleAngles ) )
- {
- *pVecTracerStart = vecMuzzle;
- }
- }
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Virtual function allows entities to handle tracer presentation
-// as they see fit.
-//
-// Input : vecTracerSrc - the point at which to start the tracer (not always the
-// same spot as the traceline!
-//
-// tr - the entire trace result for the shot.
-//
-// Output :
-//-----------------------------------------------------------------------------
-void CBaseEntity::MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType )
-{
- const char *pszTracerName = GetTracerType();
-
- Vector vNewSrc = vecTracerSrc;
-
- int iAttachment = GetTracerAttachment();
-
- switch ( iTracerType )
- {
- case TRACER_LINE:
- UTIL_Tracer( vNewSrc, tr.endpos, entindex(), iAttachment, 0.0f, false, pszTracerName );
- break;
-
- case TRACER_LINE_AND_WHIZ:
- UTIL_Tracer( vNewSrc, tr.endpos, entindex(), iAttachment, 0.0f, true, pszTracerName );
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Default tracer attachment
-//-----------------------------------------------------------------------------
-int CBaseEntity::GetTracerAttachment( void )
-{
- int iAttachment = TRACER_DONT_USE_ATTACHMENT;
-
- if ( g_pGameRules->IsMultiplayer() )
- {
- iAttachment = 1;
- }
-
- return iAttachment;
-}
-
-
-int CBaseEntity::BloodColor()
-{
- return DONT_BLEED;
-}
-
-
-void CBaseEntity::TraceBleed( float flDamage, const Vector &vecDir, trace_t *ptr, int bitsDamageType )
-{
- if ((BloodColor() == DONT_BLEED) || (BloodColor() == BLOOD_COLOR_MECH))
- {
- return;
- }
-
- if (flDamage == 0)
- return;
-
- if (! (bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_AIRBOAT)))
- return;
-
- // make blood decal on the wall!
- trace_t Bloodtr;
- Vector vecTraceDir;
- float flNoise;
- int cCount;
- int i;
-
-#ifdef GAME_DLL
- if ( !IsAlive() )
- {
- // dealing with a dead npc.
- if ( GetMaxHealth() <= 0 )
- {
- // no blood decal for a npc that has already decalled its limit.
- return;
- }
- else
- {
- m_iMaxHealth -= 1;
- }
- }
-#endif
-
- if (flDamage < 10)
- {
- flNoise = 0.1;
- cCount = 1;
- }
- else if (flDamage < 25)
- {
- flNoise = 0.2;
- cCount = 2;
- }
- else
- {
- flNoise = 0.3;
- cCount = 4;
- }
-
- float flTraceDist = (bitsDamageType & DMG_AIRBOAT) ? 384 : 172;
- for ( i = 0 ; i < cCount ; i++ )
- {
- vecTraceDir = vecDir * -1;// trace in the opposite direction the shot came from (the direction the shot is going)
-
- vecTraceDir.x += random->RandomFloat( -flNoise, flNoise );
- vecTraceDir.y += random->RandomFloat( -flNoise, flNoise );
- vecTraceDir.z += random->RandomFloat( -flNoise, flNoise );
-
- // Don't bleed on grates.
- AI_TraceLine( ptr->endpos, ptr->endpos + vecTraceDir * -flTraceDist, MASK_SOLID_BRUSHONLY & ~CONTENTS_GRATE, this, COLLISION_GROUP_NONE, &Bloodtr);
-
- if ( Bloodtr.fraction != 1.0 )
- {
- UTIL_BloodDecalTrace( &Bloodtr, BloodColor() );
- }
- }
-}
-
-
-const char* CBaseEntity::GetTracerType()
-{
- return NULL;
-}
-
-void CBaseEntity::ModifyEmitSoundParams( EmitSound_t &params )
-{
-#ifdef CLIENT_DLL
- if ( GameRules() )
- {
- params.m_pSoundName = GameRules()->TranslateEffectForVisionFilter( "sounds", params.m_pSoundName );
- }
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// These methods encapsulate MOVETYPE_FOLLOW, which became obsolete
-//-----------------------------------------------------------------------------
-void CBaseEntity::FollowEntity( CBaseEntity *pBaseEntity, bool bBoneMerge )
-{
- if (pBaseEntity)
- {
- SetParent( pBaseEntity );
- SetMoveType( MOVETYPE_NONE );
-
- if ( bBoneMerge )
- AddEffects( EF_BONEMERGE );
-
- AddSolidFlags( FSOLID_NOT_SOLID );
- SetLocalOrigin( vec3_origin );
- SetLocalAngles( vec3_angle );
- }
- else
- {
- StopFollowingEntity();
- }
-}
-
-void CBaseEntity::SetEffectEntity( CBaseEntity *pEffectEnt )
-{
- if ( m_hEffectEntity.Get() != pEffectEnt )
- {
- m_hEffectEntity = pEffectEnt;
- }
-}
-
-void CBaseEntity::ApplyLocalVelocityImpulse( const Vector &inVecImpulse )
-{
- // NOTE: Don't have to use GetVelocity here because local values
- // are always guaranteed to be correct, unlike abs values which may
- // require recomputation
- if ( inVecImpulse != vec3_origin )
- {
- Vector vecImpulse = inVecImpulse;
-
- // Safety check against receive a huge impulse, which can explode physics
- switch ( CheckEntityVelocity( vecImpulse ) )
- {
- case -1:
- Warning( "Discarding ApplyLocalVelocityImpulse(%f,%f,%f) on %s\n", vecImpulse.x, vecImpulse.y, vecImpulse.z, GetDebugName() );
- Assert( false );
- return;
- case 0:
- if ( CheckEmitReasonablePhysicsSpew() )
- {
- Warning( "Clamping ApplyLocalVelocityImpulse(%f,%f,%f) on %s\n", inVecImpulse.x, inVecImpulse.y, inVecImpulse.z, GetDebugName() );
- }
- break;
- }
-
- if ( GetMoveType() == MOVETYPE_VPHYSICS )
- {
- Vector worldVel;
- VPhysicsGetObject()->LocalToWorld( &worldVel, vecImpulse );
- VPhysicsGetObject()->AddVelocity( &worldVel, NULL );
- }
- else
- {
- InvalidatePhysicsRecursive( VELOCITY_CHANGED );
- m_vecVelocity += vecImpulse;
- }
- }
-}
-
-void CBaseEntity::ApplyAbsVelocityImpulse( const Vector &inVecImpulse )
-{
- if ( inVecImpulse != vec3_origin )
- {
- Vector vecImpulse = inVecImpulse;
-
- // Safety check against receive a huge impulse, which can explode physics
- switch ( CheckEntityVelocity( vecImpulse ) )
- {
- case -1:
- Warning( "Discarding ApplyAbsVelocityImpulse(%f,%f,%f) on %s\n", vecImpulse.x, vecImpulse.y, vecImpulse.z, GetDebugName() );
- Assert( false );
- return;
- case 0:
- if ( CheckEmitReasonablePhysicsSpew() )
- {
- Warning( "Clamping ApplyAbsVelocityImpulse(%f,%f,%f) on %s\n", inVecImpulse.x, inVecImpulse.y, inVecImpulse.z, GetDebugName() );
- }
- break;
- }
-
- if ( GetMoveType() == MOVETYPE_VPHYSICS )
- {
- VPhysicsGetObject()->AddVelocity( &vecImpulse, NULL );
- }
- else
- {
- // NOTE: Have to use GetAbsVelocity here to ensure it's the correct value
- Vector vecResult;
- VectorAdd( GetAbsVelocity(), vecImpulse, vecResult );
- SetAbsVelocity( vecResult );
- }
- }
-}
-
-void CBaseEntity::ApplyLocalAngularVelocityImpulse( const AngularImpulse &angImpulse )
-{
- if (angImpulse != vec3_origin )
- {
- // Safety check against receive a huge impulse, which can explode physics
- if ( !IsEntityAngularVelocityReasonable( angImpulse ) )
- {
- Warning( "Bad ApplyLocalAngularVelocityImpulse(%f,%f,%f) on %s\n", angImpulse.x, angImpulse.y, angImpulse.z, GetDebugName() );
- Assert( false );
- return;
- }
-
- if ( GetMoveType() == MOVETYPE_VPHYSICS )
- {
- VPhysicsGetObject()->AddVelocity( NULL, &angImpulse );
- }
- else
- {
- QAngle vecResult;
- AngularImpulseToQAngle( angImpulse, vecResult );
- VectorAdd( GetLocalAngularVelocity(), vecResult, vecResult );
- SetLocalAngularVelocity( vecResult );
- }
- }
-}
-
-void CBaseEntity::SetCollisionGroup( int collisionGroup )
-{
- if ( (int)m_CollisionGroup != collisionGroup )
- {
- m_CollisionGroup = collisionGroup;
- CollisionRulesChanged();
- }
-}
-
-
-void CBaseEntity::CollisionRulesChanged()
-{
- // ivp maintains state based on recent return values from the collision filter, so anything
- // that can change the state that a collision filter will return (like m_Solid) needs to call RecheckCollisionFilter.
- if ( VPhysicsGetObject() )
- {
- extern bool PhysIsInCallback();
- if ( PhysIsInCallback() )
- {
- Warning("Changing collision rules within a callback is likely to cause crashes!\n");
- Assert(0);
- }
- IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
- int count = VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
- for ( int i = 0; i < count; i++ )
- {
- if ( pList[i] != NULL ) //this really shouldn't happen, but it does >_<
- pList[i]->RecheckCollisionFilter();
- }
- }
-}
-
-int CBaseEntity::GetWaterType() const
-{
- int out = 0;
- if ( m_nWaterType & 1 )
- out |= CONTENTS_WATER;
- if ( m_nWaterType & 2 )
- out |= CONTENTS_SLIME;
- return out;
-}
-
-void CBaseEntity::SetWaterType( int nType )
-{
- m_nWaterType = 0;
- if ( nType & CONTENTS_WATER )
- m_nWaterType |= 1;
- if ( nType & CONTENTS_SLIME )
- m_nWaterType |= 2;
-}
-
-ConVar sv_alternateticks( "sv_alternateticks", ( IsX360() ) ? "1" : "0", FCVAR_SPONLY, "If set, server only simulates entities on even numbered ticks.\n" );
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseEntity::IsSimulatingOnAlternateTicks()
-{
- if ( gpGlobals->maxClients != 1 )
- {
- return false;
- }
-
- return sv_alternateticks.GetBool();
-}
-
-#ifdef CLIENT_DLL
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseEntity::IsToolRecording() const
-{
-#ifndef NO_TOOLFRAMEWORK
- return m_bToolRecording;
-#else
- return false;
-#endif
-}
-#endif
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+
+#include "decals.h"
+#include "effect_dispatch_data.h"
+#include "model_types.h"
+#include "gamestringpool.h"
+#include "ammodef.h"
+#include "takedamageinfo.h"
+#include "shot_manipulator.h"
+#include "ai_debug_shared.h"
+#include "mapentities_shared.h"
+#include "debugoverlay_shared.h"
+#include "coordsize.h"
+#include "vphysics/performance.h"
+
+#ifdef CLIENT_DLL
+ #include "c_te_effect_dispatch.h"
+#else
+ #include "te_effect_dispatch.h"
+ #include "soundent.h"
+ #include "iservervehicle.h"
+ #include "player_pickup.h"
+ #include "waterbullet.h"
+ #include "func_break.h"
+
+#ifdef HL2MP
+ #include "te_hl2mp_shotgun_shot.h"
+#endif
+
+ #include "gamestats.h"
+
+#endif
+
+#ifdef HL2_EPISODIC
+ConVar hl2_episodic( "hl2_episodic", "1", FCVAR_REPLICATED );
+#else
+ConVar hl2_episodic( "hl2_episodic", "0", FCVAR_REPLICATED );
+#endif//HL2_EPISODIC
+
+#ifdef PORTAL
+ #include "prop_portal_shared.h"
+#endif
+
+#ifdef TF_DLL
+#include "tf_gamerules.h"
+#include "tf_weaponbase.h"
+#endif // TF_DLL
+
+#include "rumble_shared.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#ifdef GAME_DLL
+ ConVar ent_debugkeys( "ent_debugkeys", "" );
+ extern bool ParseKeyvalue( void *pObject, typedescription_t *pFields, int iNumFields, const char *szKeyName, const char *szValue );
+ extern bool ExtractKeyvalue( void *pObject, typedescription_t *pFields, int iNumFields, const char *szKeyName, char *szValue, int iMaxLen );
+#endif
+
+bool CBaseEntity::m_bAllowPrecache = false;
+
+// Set default max values for entities based on the existing constants from elsewhere
+float k_flMaxEntityPosCoord = MAX_COORD_FLOAT;
+float k_flMaxEntityEulerAngle = 360.0 * 1000.0f; // really should be restricted to +/-180, but some code doesn't adhere to this. let's just trap NANs, etc
+// Sometimes the resulting computed speeds are legitimately above the original
+// constants; use bumped up versions for the downstream validation logic to
+// account for this.
+float k_flMaxEntitySpeed = k_flMaxVelocity * 2.0f;
+float k_flMaxEntitySpinRate = k_flMaxAngularVelocity * 10.0f;
+
+ConVar ai_shot_bias_min( "ai_shot_bias_min", "-1.0", FCVAR_REPLICATED );
+ConVar ai_shot_bias_max( "ai_shot_bias_max", "1.0", FCVAR_REPLICATED );
+ConVar ai_debug_shoot_positions( "ai_debug_shoot_positions", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
+
+// Utility func to throttle rate at which the "reasonable position" spew goes out
+static double s_LastEntityReasonableEmitTime;
+bool CheckEmitReasonablePhysicsSpew()
+{
+
+ // Reported recently?
+ double now = Plat_FloatTime();
+ if ( now >= s_LastEntityReasonableEmitTime && now < s_LastEntityReasonableEmitTime + 5.0 )
+ {
+ // Already reported recently
+ return false;
+ }
+
+ // Not reported recently. Report it now
+ s_LastEntityReasonableEmitTime = now;
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Spawn some blood particles
+//-----------------------------------------------------------------------------
+void SpawnBlood(Vector vecSpot, const Vector &vecDir, int bloodColor, float flDamage)
+{
+ UTIL_BloodDrips( vecSpot, vecDir, bloodColor, (int)flDamage );
+}
+
+#if !defined( NO_ENTITY_PREDICTION )
+//-----------------------------------------------------------------------------
+// The player drives simulation of this entity
+//-----------------------------------------------------------------------------
+void CBaseEntity::SetPlayerSimulated( CBasePlayer *pOwner )
+{
+ m_bIsPlayerSimulated = true;
+ pOwner->AddToPlayerSimulationList( this );
+ m_hPlayerSimulationOwner = pOwner;
+}
+
+void CBaseEntity::UnsetPlayerSimulated( void )
+{
+ if ( m_hPlayerSimulationOwner != NULL )
+ {
+ m_hPlayerSimulationOwner->RemoveFromPlayerSimulationList( this );
+ }
+ m_hPlayerSimulationOwner = NULL;
+ m_bIsPlayerSimulated = false;
+}
+#endif
+
+// position of eyes
+Vector CBaseEntity::EyePosition( void )
+{
+ return GetAbsOrigin() + GetViewOffset();
+}
+
+const QAngle &CBaseEntity::EyeAngles( void )
+{
+ return GetAbsAngles();
+}
+
+const QAngle &CBaseEntity::LocalEyeAngles( void )
+{
+ return GetLocalAngles();
+}
+
+// position of ears
+Vector CBaseEntity::EarPosition( void )
+{
+ return EyePosition();
+}
+
+void CBaseEntity::SetViewOffset( const Vector& v )
+{
+ m_vecViewOffset = v;
+}
+
+const Vector& CBaseEntity::GetViewOffset() const
+{
+ return m_vecViewOffset;
+}
+
+
+//-----------------------------------------------------------------------------
+// center point of entity
+//-----------------------------------------------------------------------------
+const Vector &CBaseEntity::WorldSpaceCenter( ) const
+{
+ return CollisionProp()->WorldSpaceCenter();
+}
+
+#if !defined( CLIENT_DLL )
+#define CHANGE_FLAGS(flags,newFlags) { unsigned int old = flags; flags = (newFlags); gEntList.ReportEntityFlagsChanged( this, old, flags ); }
+#else
+#define CHANGE_FLAGS(flags,newFlags) (flags = (newFlags))
+#endif
+
+void CBaseEntity::AddFlag( int flags )
+{
+ CHANGE_FLAGS( m_fFlags, m_fFlags | flags );
+}
+
+void CBaseEntity::RemoveFlag( int flagsToRemove )
+{
+ CHANGE_FLAGS( m_fFlags, m_fFlags & ~flagsToRemove );
+}
+
+void CBaseEntity::ClearFlags( void )
+{
+ CHANGE_FLAGS( m_fFlags, 0 );
+}
+
+void CBaseEntity::ToggleFlag( int flagToToggle )
+{
+ CHANGE_FLAGS( m_fFlags, m_fFlags ^ flagToToggle );
+}
+
+void CBaseEntity::SetEffects( int nEffects )
+{
+ if ( nEffects != m_fEffects )
+ {
+#if !defined( CLIENT_DLL )
+#ifdef HL2_EPISODIC
+ // Hack for now, to avoid player emitting radius with his flashlight
+ if ( !IsPlayer() )
+ {
+ if ( (nEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) && !(m_fEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) )
+ {
+ AddEntityToDarknessCheck( this );
+ }
+ else if ( !(nEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) && (m_fEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) )
+ {
+ RemoveEntityFromDarknessCheck( this );
+ }
+ }
+#endif // HL2_EPISODIC
+#endif // !CLIENT_DLL
+
+ m_fEffects = nEffects;
+
+#ifndef CLIENT_DLL
+ DispatchUpdateTransmitState();
+#else
+ UpdateVisibility();
+#endif
+ }
+}
+
+void CBaseEntity::AddEffects( int nEffects )
+{
+#if !defined( CLIENT_DLL )
+#ifdef HL2_EPISODIC
+ if ( (nEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) && !(m_fEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) )
+ {
+ // Hack for now, to avoid player emitting radius with his flashlight
+ if ( !IsPlayer() )
+ {
+ AddEntityToDarknessCheck( this );
+ }
+ }
+#endif // HL2_EPISODIC
+#endif // !CLIENT_DLL
+
+ m_fEffects |= nEffects;
+
+ if ( nEffects & EF_NODRAW)
+ {
+#ifndef CLIENT_DLL
+ DispatchUpdateTransmitState();
+#else
+ UpdateVisibility();
+#endif
+ }
+}
+
+void CBaseEntity::SetBlocksLOS( bool bBlocksLOS )
+{
+ if ( bBlocksLOS )
+ {
+ RemoveEFlags( EFL_DONTBLOCKLOS );
+ }
+ else
+ {
+ AddEFlags( EFL_DONTBLOCKLOS );
+ }
+}
+
+bool CBaseEntity::BlocksLOS( void )
+{
+ return !IsEFlagSet(EFL_DONTBLOCKLOS);
+}
+
+void CBaseEntity::SetAIWalkable( bool bBlocksLOS )
+{
+ if ( bBlocksLOS )
+ {
+ RemoveEFlags( EFL_DONTWALKON );
+ }
+ else
+ {
+ AddEFlags( EFL_DONTWALKON );
+ }
+}
+
+bool CBaseEntity::IsAIWalkable( void )
+{
+ return !IsEFlagSet(EFL_DONTWALKON);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles keys and outputs from the BSP.
+// Input : mapData - Text block of keys and values from the BSP.
+//-----------------------------------------------------------------------------
+void CBaseEntity::ParseMapData( CEntityMapData *mapData )
+{
+ char keyName[MAPKEY_MAXLENGTH];
+ char value[MAPKEY_MAXLENGTH];
+
+ #ifdef _DEBUG
+ #ifdef GAME_DLL
+ ValidateDataDescription();
+ #endif // GAME_DLL
+ #endif // _DEBUG
+
+ // loop through all keys in the data block and pass the info back into the object
+ if ( mapData->GetFirstKey(keyName, value) )
+ {
+ do
+ {
+ KeyValue( keyName, value );
+ }
+ while ( mapData->GetNextKey(keyName, value) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Parse data from a map file
+//-----------------------------------------------------------------------------
+bool CBaseEntity::KeyValue( const char *szKeyName, const char *szValue )
+{
+ //!! temp hack, until worldcraft is fixed
+ // strip the # tokens from (duplicate) key names
+ char *s = (char *)strchr( szKeyName, '#' );
+ if ( s )
+ {
+ *s = '\0';
+ }
+
+ if ( FStrEq( szKeyName, "rendercolor" ) || FStrEq( szKeyName, "rendercolor32" ))
+ {
+ color32 tmp;
+ UTIL_StringToColor32( &tmp, szValue );
+ SetRenderColor( tmp.r, tmp.g, tmp.b );
+ // don't copy alpha, legacy support uses renderamt
+ return true;
+ }
+
+ if ( FStrEq( szKeyName, "renderamt" ) )
+ {
+ SetRenderColorA( atoi( szValue ) );
+ return true;
+ }
+
+ if ( FStrEq( szKeyName, "disableshadows" ))
+ {
+ int val = atoi( szValue );
+ if (val)
+ {
+ AddEffects( EF_NOSHADOW );
+ }
+ return true;
+ }
+
+ if ( FStrEq( szKeyName, "mins" ))
+ {
+ Vector mins;
+ UTIL_StringToVector( mins.Base(), szValue );
+ CollisionProp()->SetCollisionBounds( mins, CollisionProp()->OBBMaxs() );
+ return true;
+ }
+
+ if ( FStrEq( szKeyName, "maxs" ))
+ {
+ Vector maxs;
+ UTIL_StringToVector( maxs.Base(), szValue );
+ CollisionProp()->SetCollisionBounds( CollisionProp()->OBBMins(), maxs );
+ return true;
+ }
+
+ if ( FStrEq( szKeyName, "disablereceiveshadows" ))
+ {
+ int val = atoi( szValue );
+ if (val)
+ {
+ AddEffects( EF_NORECEIVESHADOW );
+ }
+ return true;
+ }
+
+ if ( FStrEq( szKeyName, "nodamageforces" ))
+ {
+ int val = atoi( szValue );
+ if (val)
+ {
+ AddEFlags( EFL_NO_DAMAGE_FORCES );
+ }
+ return true;
+ }
+
+ // Fix up single angles
+ if( FStrEq( szKeyName, "angle" ) )
+ {
+ static char szBuf[64];
+
+ float y = atof( szValue );
+ if (y >= 0)
+ {
+ Q_snprintf( szBuf,sizeof(szBuf), "%f %f %f", GetLocalAngles()[0], y, GetLocalAngles()[2] );
+ }
+ else if ((int)y == -1)
+ {
+ Q_strncpy( szBuf, "-90 0 0", sizeof(szBuf) );
+ }
+ else
+ {
+ Q_strncpy( szBuf, "90 0 0", sizeof(szBuf) );
+ }
+
+ // Do this so inherited classes looking for 'angles' don't have to bother with 'angle'
+ return KeyValue( szKeyName, szBuf );
+ }
+
+ // NOTE: Have to do these separate because they set two values instead of one
+ if( FStrEq( szKeyName, "angles" ) )
+ {
+ QAngle angles;
+ UTIL_StringToVector( angles.Base(), szValue );
+
+ // If you're hitting this assert, it's probably because you're
+ // calling SetLocalAngles from within a KeyValues method.. use SetAbsAngles instead!
+ Assert( (GetMoveParent() == NULL) && !IsEFlagSet( EFL_DIRTY_ABSTRANSFORM ) );
+ SetAbsAngles( angles );
+ return true;
+ }
+
+ if( FStrEq( szKeyName, "origin" ) )
+ {
+ Vector vecOrigin;
+ UTIL_StringToVector( vecOrigin.Base(), szValue );
+
+ // If you're hitting this assert, it's probably because you're
+ // calling SetLocalOrigin from within a KeyValues method.. use SetAbsOrigin instead!
+ Assert( (GetMoveParent() == NULL) && !IsEFlagSet( EFL_DIRTY_ABSTRANSFORM ) );
+ SetAbsOrigin( vecOrigin );
+ return true;
+ }
+
+#ifdef GAME_DLL
+
+ if ( FStrEq( szKeyName, "targetname" ) )
+ {
+ m_iName = AllocPooledString( szValue );
+ return true;
+ }
+
+ // loop through the data description, and try and place the keys in
+ if ( !*ent_debugkeys.GetString() )
+ {
+ for ( datamap_t *dmap = GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
+ {
+ if ( ::ParseKeyvalue(this, dmap->dataDesc, dmap->dataNumFields, szKeyName, szValue) )
+ return true;
+ }
+ }
+ else
+ {
+ // debug version - can be used to see what keys have been parsed in
+ bool printKeyHits = false;
+ const char *debugName = "";
+
+ if ( *ent_debugkeys.GetString() && !Q_stricmp(ent_debugkeys.GetString(), STRING(m_iClassname)) )
+ {
+ // Msg( "-- found entity of type %s\n", STRING(m_iClassname) );
+ printKeyHits = true;
+ debugName = STRING(m_iClassname);
+ }
+
+ // loop through the data description, and try and place the keys in
+ for ( datamap_t *dmap = GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
+ {
+ if ( !printKeyHits && *ent_debugkeys.GetString() && !Q_stricmp(dmap->dataClassName, ent_debugkeys.GetString()) )
+ {
+ // Msg( "-- found class of type %s\n", dmap->dataClassName );
+ printKeyHits = true;
+ debugName = dmap->dataClassName;
+ }
+
+ if ( ::ParseKeyvalue(this, dmap->dataDesc, dmap->dataNumFields, szKeyName, szValue) )
+ {
+ if ( printKeyHits )
+ Msg( "(%s) key: %-16s value: %s\n", debugName, szKeyName, szValue );
+
+ return true;
+ }
+ }
+
+ if ( printKeyHits )
+ Msg( "!! (%s) key not handled: \"%s\" \"%s\"\n", STRING(m_iClassname), szKeyName, szValue );
+ }
+
+#endif
+
+ // key hasn't been handled
+ return false;
+}
+
+bool CBaseEntity::KeyValue( const char *szKeyName, float flValue )
+{
+ char string[256];
+
+ Q_snprintf(string,sizeof(string), "%f", flValue );
+
+ return KeyValue( szKeyName, string );
+}
+
+bool CBaseEntity::KeyValue( const char *szKeyName, const Vector &vecValue )
+{
+ char string[256];
+
+ Q_snprintf(string,sizeof(string), "%f %f %f", vecValue.x, vecValue.y, vecValue.z );
+
+ return KeyValue( szKeyName, string );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+
+bool CBaseEntity::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen )
+{
+ if ( FStrEq( szKeyName, "rendercolor" ) || FStrEq( szKeyName, "rendercolor32" ))
+ {
+ color32 tmp = GetRenderColor();
+ Q_snprintf( szValue, iMaxLen, "%d %d %d %d", tmp.r, tmp.g, tmp.b, tmp.a );
+ return true;
+ }
+
+ if ( FStrEq( szKeyName, "renderamt" ) )
+ {
+ color32 tmp = GetRenderColor();
+ Q_snprintf( szValue, iMaxLen, "%d", tmp.a );
+ return true;
+ }
+
+ if ( FStrEq( szKeyName, "disableshadows" ))
+ {
+ Q_snprintf( szValue, iMaxLen, "%d", IsEffectActive( EF_NOSHADOW ) );
+ return true;
+ }
+
+ if ( FStrEq( szKeyName, "mins" ))
+ {
+ Assert( 0 );
+ return false;
+ }
+
+ if ( FStrEq( szKeyName, "maxs" ))
+ {
+ Assert( 0 );
+ return false;
+ }
+
+ if ( FStrEq( szKeyName, "disablereceiveshadows" ))
+ {
+ Q_snprintf( szValue, iMaxLen, "%d", IsEffectActive( EF_NORECEIVESHADOW ) );
+ return true;
+ }
+
+ if ( FStrEq( szKeyName, "nodamageforces" ))
+ {
+ Q_snprintf( szValue, iMaxLen, "%d", IsEffectActive( EFL_NO_DAMAGE_FORCES ) );
+ return true;
+ }
+
+ // Fix up single angles
+ if( FStrEq( szKeyName, "angle" ) )
+ {
+ return false;
+ }
+
+ // NOTE: Have to do these separate because they set two values instead of one
+ if( FStrEq( szKeyName, "angles" ) )
+ {
+ QAngle angles = GetAbsAngles();
+
+ Q_snprintf( szValue, iMaxLen, "%f %f %f", angles.x, angles.y, angles.z );
+ return true;
+ }
+
+ if( FStrEq( szKeyName, "origin" ) )
+ {
+ Vector vecOrigin = GetAbsOrigin();
+ Q_snprintf( szValue, iMaxLen, "%f %f %f", vecOrigin.x, vecOrigin.y, vecOrigin.z );
+ return true;
+ }
+
+#ifdef GAME_DLL
+
+ if ( FStrEq( szKeyName, "targetname" ) )
+ {
+ Q_snprintf( szValue, iMaxLen, "%s", STRING( GetEntityName() ) );
+ return true;
+ }
+
+ if ( FStrEq( szKeyName, "classname" ) )
+ {
+ Q_snprintf( szValue, iMaxLen, "%s", GetClassname() );
+ return true;
+ }
+
+ for ( datamap_t *dmap = GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
+ {
+ if ( ::ExtractKeyvalue( this, dmap->dataDesc, dmap->dataNumFields, szKeyName, szValue, iMaxLen ) )
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : collisionGroup -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseEntity::ShouldCollide( int collisionGroup, int contentsMask ) const
+{
+ if ( m_CollisionGroup == COLLISION_GROUP_DEBRIS )
+ {
+ if ( ! (contentsMask & CONTENTS_DEBRIS) )
+ return false;
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : seed -
+//-----------------------------------------------------------------------------
+void CBaseEntity::SetPredictionRandomSeed( const CUserCmd *cmd )
+{
+ if ( !cmd )
+ {
+ m_nPredictionRandomSeed = -1;
+ return;
+ }
+
+ m_nPredictionRandomSeed = ( cmd->random_seed );
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose : Base implimentation for entity handling decals
+//------------------------------------------------------------------------------
+void CBaseEntity::DecalTrace( trace_t *pTrace, char const *decalName )
+{
+ int index = decalsystem->GetDecalIndexForName( decalName );
+ if ( index < 0 )
+ return;
+
+ Assert( pTrace->m_pEnt );
+
+ CBroadcastRecipientFilter filter;
+ te->Decal( filter, 0.0, &pTrace->endpos, &pTrace->startpos,
+ pTrace->GetEntityIndex(), pTrace->hitbox, index );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Base handling for impacts against entities
+//-----------------------------------------------------------------------------
+void CBaseEntity::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
+{
+ VPROF( "CBaseEntity::ImpactTrace" );
+ Assert( pTrace->m_pEnt );
+
+ CBaseEntity *pEntity = pTrace->m_pEnt;
+
+ // Build the impact data
+ CEffectData data;
+ data.m_vOrigin = pTrace->endpos;
+ data.m_vStart = pTrace->startpos;
+ data.m_nSurfaceProp = pTrace->surface.surfaceProps;
+ data.m_nDamageType = iDamageType;
+ data.m_nHitBox = pTrace->hitbox;
+#ifdef CLIENT_DLL
+ data.m_hEntity = ClientEntityList().EntIndexToHandle( pEntity->entindex() );
+#else
+ data.m_nEntIndex = pEntity->entindex();
+#endif
+
+ // Send it on its way
+ if ( !pCustomImpactName )
+ {
+ DispatchEffect( "Impact", data );
+ }
+ else
+ {
+ DispatchEffect( pCustomImpactName, data );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the damage decal to use, given a damage type
+// Input : bitsDamageType - the damage type
+// Output : the index of the damage decal to use
+//-----------------------------------------------------------------------------
+char const *CBaseEntity::DamageDecal( int bitsDamageType, int gameMaterial )
+{
+ if ( m_nRenderMode == kRenderTransAlpha )
+ return "";
+
+ if ( m_nRenderMode != kRenderNormal && gameMaterial == 'G' )
+ return "BulletProof";
+
+ if ( bitsDamageType == DMG_SLASH )
+ return "ManhackCut";
+
+ // This will get translated at a lower layer based on game material
+ return "Impact.Concrete";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseEntity::GetIndexForThinkContext( const char *pszContext )
+{
+ for ( int i = 0; i < m_aThinkFunctions.Size(); i++ )
+ {
+ if ( !Q_strncmp( STRING( m_aThinkFunctions[i].m_iszContext ), pszContext, MAX_CONTEXT_LENGTH ) )
+ return i;
+ }
+
+ return NO_THINK_CONTEXT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a fresh think context for this entity
+//-----------------------------------------------------------------------------
+int CBaseEntity::RegisterThinkContext( const char *szContext )
+{
+ int iIndex = GetIndexForThinkContext( szContext );
+ if ( iIndex != NO_THINK_CONTEXT )
+ return iIndex;
+
+ // Make a new think func
+ thinkfunc_t sNewFunc;
+ Q_memset( &sNewFunc, 0, sizeof( sNewFunc ) );
+ sNewFunc.m_pfnThink = NULL;
+ sNewFunc.m_nNextThinkTick = 0;
+ sNewFunc.m_iszContext = AllocPooledString(szContext);
+
+ // Insert it into our list
+ return m_aThinkFunctions.AddToTail( sNewFunc );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+BASEPTR CBaseEntity::ThinkSet( BASEPTR func, float thinkTime, const char *szContext )
+{
+#if !defined( CLIENT_DLL )
+#ifdef _DEBUG
+#ifdef GNUC
+ COMPILE_TIME_ASSERT( sizeof(func) == 8 );
+#else
+ COMPILE_TIME_ASSERT( sizeof(func) == 4 );
+#endif
+#endif
+#endif
+
+ // Old system?
+ if ( !szContext )
+ {
+ m_pfnThink = func;
+#if !defined( CLIENT_DLL )
+#ifdef _DEBUG
+ FunctionCheck( *(reinterpret_cast<void **>(&m_pfnThink)), "BaseThinkFunc" );
+#endif
+#endif
+ return m_pfnThink;
+ }
+
+ // Find the think function in our list, and if we couldn't find it, register it
+ int iIndex = GetIndexForThinkContext( szContext );
+ if ( iIndex == NO_THINK_CONTEXT )
+ {
+ iIndex = RegisterThinkContext( szContext );
+ }
+
+ m_aThinkFunctions[ iIndex ].m_pfnThink = func;
+#if !defined( CLIENT_DLL )
+#ifdef _DEBUG
+ FunctionCheck( *(reinterpret_cast<void **>(&m_aThinkFunctions[ iIndex ].m_pfnThink)), szContext );
+#endif
+#endif
+
+ if ( thinkTime != 0 )
+ {
+ int thinkTick = ( thinkTime == TICK_NEVER_THINK ) ? TICK_NEVER_THINK : TIME_TO_TICKS( thinkTime );
+ m_aThinkFunctions[ iIndex ].m_nNextThinkTick = thinkTick;
+ CheckHasThinkFunction( thinkTick == TICK_NEVER_THINK ? false : true );
+ }
+ return func;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseEntity::SetNextThink( float thinkTime, const char *szContext )
+{
+ int thinkTick = ( thinkTime == TICK_NEVER_THINK ) ? TICK_NEVER_THINK : TIME_TO_TICKS( thinkTime );
+
+ // Are we currently in a think function with a context?
+ int iIndex = 0;
+ if ( !szContext )
+ {
+#ifdef _DEBUG
+ if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
+ {
+ Msg( "Warning: Setting base think function within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
+ }
+#endif
+
+ // Old system
+ m_nNextThinkTick = thinkTick;
+ CheckHasThinkFunction( thinkTick == TICK_NEVER_THINK ? false : true );
+ return;
+ }
+ else
+ {
+ // Find the think function in our list, and if we couldn't find it, register it
+ iIndex = GetIndexForThinkContext( szContext );
+ if ( iIndex == NO_THINK_CONTEXT )
+ {
+ iIndex = RegisterThinkContext( szContext );
+ }
+ }
+
+ // Old system
+ m_aThinkFunctions[ iIndex ].m_nNextThinkTick = thinkTick;
+ CheckHasThinkFunction( thinkTick == TICK_NEVER_THINK ? false : true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CBaseEntity::GetNextThink( const char *szContext )
+{
+ // Are we currently in a think function with a context?
+ int iIndex = 0;
+ if ( !szContext )
+ {
+#ifdef _DEBUG
+ if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
+ {
+ Msg( "Warning: Getting base nextthink time within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
+ }
+#endif
+
+ if ( m_nNextThinkTick == TICK_NEVER_THINK )
+ return TICK_NEVER_THINK;
+
+ // Old system
+ return TICK_INTERVAL * (m_nNextThinkTick );
+ }
+ else
+ {
+ // Find the think function in our list
+ iIndex = GetIndexForThinkContext( szContext );
+ }
+
+ if ( iIndex == m_aThinkFunctions.InvalidIndex() )
+ return TICK_NEVER_THINK;
+
+ if ( m_aThinkFunctions[ iIndex ].m_nNextThinkTick == TICK_NEVER_THINK )
+ {
+ return TICK_NEVER_THINK;
+ }
+ return TICK_INTERVAL * (m_aThinkFunctions[ iIndex ].m_nNextThinkTick );
+}
+
+int CBaseEntity::GetNextThinkTick( const char *szContext /*= NULL*/ )
+{
+ // Are we currently in a think function with a context?
+ int iIndex = 0;
+ if ( !szContext )
+ {
+#ifdef _DEBUG
+ if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
+ {
+ Msg( "Warning: Getting base nextthink time within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
+ }
+#endif
+
+ if ( m_nNextThinkTick == TICK_NEVER_THINK )
+ return TICK_NEVER_THINK;
+
+ // Old system
+ return m_nNextThinkTick;
+ }
+ else
+ {
+ // Find the think function in our list
+ iIndex = GetIndexForThinkContext( szContext );
+
+ // Looking up an invalid think context!
+ Assert( iIndex != -1 );
+ }
+
+ if ( ( iIndex == -1 ) || ( m_aThinkFunctions[ iIndex ].m_nNextThinkTick == TICK_NEVER_THINK ) )
+ {
+ return TICK_NEVER_THINK;
+ }
+
+ return m_aThinkFunctions[ iIndex ].m_nNextThinkTick;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CBaseEntity::GetLastThink( const char *szContext )
+{
+ // Are we currently in a think function with a context?
+ int iIndex = 0;
+ if ( !szContext )
+ {
+#ifdef _DEBUG
+ if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
+ {
+ Msg( "Warning: Getting base lastthink time within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
+ }
+#endif
+ // Old system
+ return m_nLastThinkTick * TICK_INTERVAL;
+ }
+ else
+ {
+ // Find the think function in our list
+ iIndex = GetIndexForThinkContext( szContext );
+ }
+
+ return m_aThinkFunctions[ iIndex ].m_nLastThinkTick * TICK_INTERVAL;
+}
+
+int CBaseEntity::GetLastThinkTick( const char *szContext /*= NULL*/ )
+{
+ // Are we currently in a think function with a context?
+ int iIndex = 0;
+ if ( !szContext )
+ {
+#ifdef _DEBUG
+ if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
+ {
+ Msg( "Warning: Getting base lastthink time within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
+ }
+#endif
+ // Old system
+ return m_nLastThinkTick;
+ }
+ else
+ {
+ // Find the think function in our list
+ iIndex = GetIndexForThinkContext( szContext );
+ }
+
+ return m_aThinkFunctions[ iIndex ].m_nLastThinkTick;
+}
+
+bool CBaseEntity::WillThink()
+{
+ if ( m_nNextThinkTick > 0 )
+ return true;
+
+ for ( int i = 0; i < m_aThinkFunctions.Count(); i++ )
+ {
+ if ( m_aThinkFunctions[i].m_nNextThinkTick > 0 )
+ return true;
+ }
+
+ return false;
+}
+
+// returns the first tick the entity will run any think function
+// returns TICK_NEVER_THINK if no think functions are scheduled
+int CBaseEntity::GetFirstThinkTick()
+{
+ int minTick = TICK_NEVER_THINK;
+ if ( m_nNextThinkTick > 0 )
+ {
+ minTick = m_nNextThinkTick;
+ }
+
+ for ( int i = 0; i < m_aThinkFunctions.Count(); i++ )
+ {
+ int next = m_aThinkFunctions[i].m_nNextThinkTick;
+ if ( next > 0 )
+ {
+ if ( next < minTick || minTick == TICK_NEVER_THINK )
+ {
+ minTick = next;
+ }
+ }
+ }
+ return minTick;
+}
+
+// NOTE: pass in the isThinking hint so we have to search the think functions less
+void CBaseEntity::CheckHasThinkFunction( bool isThinking )
+{
+ if ( IsEFlagSet( EFL_NO_THINK_FUNCTION ) && isThinking )
+ {
+ RemoveEFlags( EFL_NO_THINK_FUNCTION );
+ }
+ else if ( !isThinking && !IsEFlagSet( EFL_NO_THINK_FUNCTION ) && !WillThink() )
+ {
+ AddEFlags( EFL_NO_THINK_FUNCTION );
+ }
+#if !defined( CLIENT_DLL )
+ SimThink_EntityChanged( this );
+#endif
+}
+
+bool CBaseEntity::WillSimulateGamePhysics()
+{
+ // players always simulate game physics
+ if ( !IsPlayer() )
+ {
+ MoveType_t movetype = GetMoveType();
+
+ if ( movetype == MOVETYPE_NONE || movetype == MOVETYPE_VPHYSICS )
+ return false;
+
+#if !defined( CLIENT_DLL )
+ // MOVETYPE_PUSH not supported on the client
+ if ( movetype == MOVETYPE_PUSH && GetMoveDoneTime() <= 0 )
+ return false;
+#endif
+ }
+
+ return true;
+}
+
+void CBaseEntity::CheckHasGamePhysicsSimulation()
+{
+ bool isSimulating = WillSimulateGamePhysics();
+ if ( isSimulating != IsEFlagSet(EFL_NO_GAME_PHYSICS_SIMULATION) )
+ return;
+ if ( isSimulating )
+ {
+ RemoveEFlags( EFL_NO_GAME_PHYSICS_SIMULATION );
+ }
+ else
+ {
+ AddEFlags( EFL_NO_GAME_PHYSICS_SIMULATION );
+ }
+#if !defined( CLIENT_DLL )
+ SimThink_EntityChanged( this );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Sets/Gets the next think based on context index
+//-----------------------------------------------------------------------------
+void CBaseEntity::SetNextThink( int nContextIndex, float thinkTime )
+{
+ int thinkTick = ( thinkTime == TICK_NEVER_THINK ) ? TICK_NEVER_THINK : TIME_TO_TICKS( thinkTime );
+
+ if (nContextIndex < 0)
+ {
+ SetNextThink( thinkTime );
+ }
+ else
+ {
+ m_aThinkFunctions[nContextIndex].m_nNextThinkTick = thinkTick;
+ }
+ CheckHasThinkFunction( thinkTick == TICK_NEVER_THINK ? false : true );
+}
+
+void CBaseEntity::SetLastThink( int nContextIndex, float thinkTime )
+{
+ int thinkTick = ( thinkTime == TICK_NEVER_THINK ) ? TICK_NEVER_THINK : TIME_TO_TICKS( thinkTime );
+
+ if (nContextIndex < 0)
+ {
+ m_nLastThinkTick = thinkTick;
+ }
+ else
+ {
+ m_aThinkFunctions[nContextIndex].m_nLastThinkTick = thinkTick;
+ }
+}
+
+float CBaseEntity::GetNextThink( int nContextIndex ) const
+{
+ if (nContextIndex < 0)
+ return m_nNextThinkTick * TICK_INTERVAL;
+
+ return m_aThinkFunctions[nContextIndex].m_nNextThinkTick * TICK_INTERVAL;
+}
+
+int CBaseEntity::GetNextThinkTick( int nContextIndex ) const
+{
+ if (nContextIndex < 0)
+ return m_nNextThinkTick;
+
+ return m_aThinkFunctions[nContextIndex].m_nNextThinkTick;
+}
+
+
+int CheckEntityVelocity( Vector &v )
+{
+ float r = k_flMaxEntitySpeed;
+ if (
+ v.x > -r && v.x < r &&
+ v.y > -r && v.y < r &&
+ v.z > -r && v.z < r)
+ {
+ // The usual case. It's totally reasonable
+ return 1;
+ }
+ float speed = v.Length();
+ if ( speed < k_flMaxEntitySpeed * 100.0f )
+ {
+ // Sort of suspicious. Clamp it
+ v *= k_flMaxEntitySpeed / speed;
+ return 0;
+ }
+
+ // A terrible, horrible, no good, very bad velocity.
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: My physics object has been updated, react or extract data
+//-----------------------------------------------------------------------------
+void CBaseEntity::VPhysicsUpdate( IPhysicsObject *pPhysics )
+{
+ switch( GetMoveType() )
+ {
+ case MOVETYPE_VPHYSICS:
+ {
+ if ( GetMoveParent() )
+ {
+ DevWarning("Updating physics on object in hierarchy %s!\n", GetClassname());
+ return;
+ }
+ Vector origin;
+ QAngle angles;
+
+ pPhysics->GetPosition( &origin, &angles );
+
+ if ( !IsEntityQAngleReasonable( angles ) )
+ {
+ if ( CheckEmitReasonablePhysicsSpew() )
+ {
+ Warning( "Ignoring bogus angles (%f,%f,%f) from vphysics! (entity %s)\n", angles.x, angles.y, angles.z, GetDebugName() );
+ }
+ angles = vec3_angle;
+ }
+#ifndef CLIENT_DLL
+ Vector prevOrigin = GetAbsOrigin();
+#endif
+
+ if ( IsEntityPositionReasonable( origin ) )
+ {
+ SetAbsOrigin( origin );
+ }
+ else
+ {
+ if ( CheckEmitReasonablePhysicsSpew() )
+ {
+ Warning( "Ignoring unreasonable position (%f,%f,%f) from vphysics! (entity %s)\n", origin.x, origin.y, origin.z, GetDebugName() );
+ }
+ }
+
+ for ( int i = 0; i < 3; ++i )
+ {
+ angles[ i ] = AngleNormalize( angles[ i ] );
+ }
+ SetAbsAngles( angles );
+
+ // Interactive debris converts back to debris when it comes to rest
+ if ( pPhysics->IsAsleep() && GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
+ {
+ SetCollisionGroup( COLLISION_GROUP_DEBRIS );
+ }
+
+#ifndef CLIENT_DLL
+ PhysicsTouchTriggers( &prevOrigin );
+ PhysicsRelinkChildren(gpGlobals->frametime);
+#endif
+ }
+ break;
+
+ case MOVETYPE_STEP:
+ break;
+
+ case MOVETYPE_PUSH:
+#ifndef CLIENT_DLL
+ VPhysicsUpdatePusher( pPhysics );
+#endif
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Init this object's physics as a static
+//-----------------------------------------------------------------------------
+IPhysicsObject *CBaseEntity::VPhysicsInitStatic( void )
+{
+ if ( !VPhysicsInitSetup() )
+ return NULL;
+
+#ifndef CLIENT_DLL
+ // If this entity has a move parent, it needs to be shadow, not static
+ if ( GetMoveParent() )
+ {
+ // must be SOLID_VPHYSICS if in hierarchy to solve collisions correctly
+ if ( GetSolid() == SOLID_BSP && GetRootMoveParent()->GetSolid() != SOLID_BSP )
+ {
+ SetSolid( SOLID_VPHYSICS );
+ }
+
+ return VPhysicsInitShadow( false, false );
+ }
+#endif
+
+ // No physics
+ if ( GetSolid() == SOLID_NONE )
+ return NULL;
+
+ // create a static physics objct
+ IPhysicsObject *pPhysicsObject = NULL;
+ if ( GetSolid() == SOLID_BBOX )
+ {
+ pPhysicsObject = PhysModelCreateBox( this, WorldAlignMins(), WorldAlignMaxs(), GetAbsOrigin(), true );
+ }
+ else
+ {
+ pPhysicsObject = PhysModelCreateUnmoveable( this, GetModelIndex(), GetAbsOrigin(), GetAbsAngles() );
+ }
+ VPhysicsSetObject( pPhysicsObject );
+ return pPhysicsObject;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPhysics -
+//-----------------------------------------------------------------------------
+void CBaseEntity::VPhysicsSetObject( IPhysicsObject *pPhysics )
+{
+ if ( m_pPhysicsObject && pPhysics )
+ {
+ Warning( "Overwriting physics object for %s\n", GetClassname() );
+ }
+ m_pPhysicsObject = pPhysics;
+ if ( pPhysics && !m_pPhysicsObject )
+ {
+ CollisionRulesChanged();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseEntity::VPhysicsDestroyObject( void )
+{
+ if ( m_pPhysicsObject )
+ {
+#ifndef CLIENT_DLL
+ PhysRemoveShadow( this );
+#endif
+ PhysDestroyObject( m_pPhysicsObject, this );
+ m_pPhysicsObject = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseEntity::VPhysicsInitSetup()
+{
+#ifndef CLIENT_DLL
+ // don't support logical ents
+ if ( !edict() || IsMarkedForDeletion() )
+ return false;
+#endif
+
+ // If this entity already has a physics object, then it should have been deleted prior to making this call.
+ Assert(!m_pPhysicsObject);
+ VPhysicsDestroyObject();
+
+ // make sure absorigin / absangles are correct
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: This creates a normal vphysics simulated object
+// physics alone determines where it goes (gravity, friction, etc)
+// and the entity receives updates from vphysics. SetAbsOrigin(), etc do not affect the object!
+//-----------------------------------------------------------------------------
+IPhysicsObject *CBaseEntity::VPhysicsInitNormal( SolidType_t solidType, int nSolidFlags, bool createAsleep, solid_t *pSolid )
+{
+ if ( !VPhysicsInitSetup() )
+ return NULL;
+
+ // NOTE: This has to occur before PhysModelCreate because that call will
+ // call back into ShouldCollide(), which uses solidtype for rules.
+ SetSolid( solidType );
+ SetSolidFlags( nSolidFlags );
+
+ // No physics
+ if ( solidType == SOLID_NONE )
+ {
+ return NULL;
+ }
+
+ // create a normal physics object
+ IPhysicsObject *pPhysicsObject = PhysModelCreate( this, GetModelIndex(), GetAbsOrigin(), GetAbsAngles(), pSolid );
+ if ( pPhysicsObject )
+ {
+ VPhysicsSetObject( pPhysicsObject );
+ SetMoveType( MOVETYPE_VPHYSICS );
+
+ if ( !createAsleep )
+ {
+ pPhysicsObject->Wake();
+ }
+ }
+
+ return pPhysicsObject;
+}
+
+// This creates a vphysics object with a shadow controller that follows the AI
+IPhysicsObject *CBaseEntity::VPhysicsInitShadow( bool allowPhysicsMovement, bool allowPhysicsRotation, solid_t *pSolid )
+{
+ if ( !VPhysicsInitSetup() )
+ return NULL;
+
+ // No physics
+ if ( GetSolid() == SOLID_NONE )
+ return NULL;
+
+ const Vector &origin = GetAbsOrigin();
+ QAngle angles = GetAbsAngles();
+ IPhysicsObject *pPhysicsObject = NULL;
+
+ if ( GetSolid() == SOLID_BBOX )
+ {
+ // adjust these so the game tracing epsilons match the physics minimum separation distance
+ // this will shrink the vphysics version of the model by the difference in epsilons
+ float radius = 0.25f - DIST_EPSILON;
+ Vector mins = WorldAlignMins() + Vector(radius, radius, radius);
+ Vector maxs = WorldAlignMaxs() - Vector(radius, radius, radius);
+ pPhysicsObject = PhysModelCreateBox( this, mins, maxs, origin, false );
+ angles = vec3_angle;
+ }
+ else if ( GetSolid() == SOLID_OBB )
+ {
+ pPhysicsObject = PhysModelCreateOBB( this, CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), origin, angles, false );
+ }
+ else
+ {
+ pPhysicsObject = PhysModelCreate( this, GetModelIndex(), origin, angles, pSolid );
+ }
+ if ( !pPhysicsObject )
+ return NULL;
+
+ VPhysicsSetObject( pPhysicsObject );
+ // UNDONE: Tune these speeds!!!
+ pPhysicsObject->SetShadow( 1e4, 1e4, allowPhysicsMovement, allowPhysicsRotation );
+ pPhysicsObject->UpdateShadow( origin, angles, false, 0 );
+ return pPhysicsObject;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseEntity::CreateVPhysics()
+{
+ return false;
+}
+
+bool CBaseEntity::IsStandable() const
+{
+ if (GetSolidFlags() & FSOLID_NOT_STANDABLE)
+ return false;
+
+ if ( GetSolid() == SOLID_BSP || GetSolid() == SOLID_VPHYSICS || GetSolid() == SOLID_BBOX )
+ return true;
+
+ return IsBSPModel( );
+}
+
+bool CBaseEntity::IsBSPModel() const
+{
+ if ( GetSolid() == SOLID_BSP )
+ return true;
+
+ const model_t *model = modelinfo->GetModel( GetModelIndex() );
+
+ if ( GetSolid() == SOLID_VPHYSICS && modelinfo->GetModelType( model ) == mod_brush )
+ return true;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Invalidates the abs state of all children
+//-----------------------------------------------------------------------------
+void CBaseEntity::InvalidatePhysicsRecursive( int nChangeFlags )
+{
+ // Main entry point for dirty flag setting for the 90% case
+ // 1) If the origin changes, then we have to update abstransform, Shadow projection, PVS, KD-tree,
+ // client-leaf system.
+ // 2) If the angles change, then we have to update abstransform, Shadow projection,
+ // shadow render-to-texture, client-leaf system, and surrounding bounds.
+ // Children have to additionally update absvelocity, KD-tree, and PVS.
+ // If the surrounding bounds actually update, when we also need to update the KD-tree and the PVS.
+ // 3) If it's due to attachment, then all children who are attached to an attachment point
+ // are assumed to have dirty origin + angles.
+
+ // Other stuff:
+ // 1) Marking the surrounding bounds dirty will automatically mark KD tree + PVS dirty.
+
+ int nDirtyFlags = 0;
+
+ if ( nChangeFlags & VELOCITY_CHANGED )
+ {
+ nDirtyFlags |= EFL_DIRTY_ABSVELOCITY;
+ }
+
+ if ( nChangeFlags & POSITION_CHANGED )
+ {
+ nDirtyFlags |= EFL_DIRTY_ABSTRANSFORM;
+
+#ifndef CLIENT_DLL
+ NetworkProp()->MarkPVSInformationDirty();
+#endif
+
+ // NOTE: This will also mark shadow projection + client leaf dirty
+ CollisionProp()->MarkPartitionHandleDirty();
+ }
+
+ // NOTE: This has to be done after velocity + position are changed
+ // because we change the nChangeFlags for the child entities
+ if ( nChangeFlags & ANGLES_CHANGED )
+ {
+ nDirtyFlags |= EFL_DIRTY_ABSTRANSFORM;
+ if ( CollisionProp()->DoesRotationInvalidateSurroundingBox() )
+ {
+ // NOTE: This will handle the KD-tree, surrounding bounds, PVS
+ // render-to-texture shadow, shadow projection, and client leaf dirty
+ CollisionProp()->MarkSurroundingBoundsDirty();
+ }
+ else
+ {
+#ifdef CLIENT_DLL
+ MarkRenderHandleDirty();
+ g_pClientShadowMgr->AddToDirtyShadowList( this );
+ g_pClientShadowMgr->MarkRenderToTextureShadowDirty( GetShadowHandle() );
+#endif
+ }
+
+ // This is going to be used for all children: children
+ // have position + velocity changed
+ nChangeFlags |= POSITION_CHANGED | VELOCITY_CHANGED;
+ }
+
+ AddEFlags( nDirtyFlags );
+
+ // Set flags for children
+ bool bOnlyDueToAttachment = false;
+ if ( nChangeFlags & ANIMATION_CHANGED )
+ {
+#ifdef CLIENT_DLL
+ g_pClientShadowMgr->MarkRenderToTextureShadowDirty( GetShadowHandle() );
+#endif
+
+ // Only set this flag if the only thing that changed us was the animation.
+ // If position or something else changed us, then we must tell all children.
+ if ( !( nChangeFlags & (POSITION_CHANGED | VELOCITY_CHANGED | ANGLES_CHANGED) ) )
+ {
+ bOnlyDueToAttachment = true;
+ }
+
+ nChangeFlags = POSITION_CHANGED | ANGLES_CHANGED | VELOCITY_CHANGED;
+ }
+
+ for (CBaseEntity *pChild = FirstMoveChild(); pChild; pChild = pChild->NextMovePeer())
+ {
+ // If this is due to the parent animating, only invalidate children that are parented to an attachment
+ // Entities that are following also access attachments points on parents and must be invalidated.
+ if ( bOnlyDueToAttachment )
+ {
+#ifdef CLIENT_DLL
+ if ( (pChild->GetParentAttachment() == 0) && !pChild->IsFollowingEntity() )
+ continue;
+#else
+ if ( pChild->GetParentAttachment() == 0 )
+ continue;
+#endif
+ }
+ pChild->InvalidatePhysicsRecursive( nChangeFlags );
+ }
+
+ //
+ // This code should really be in here, or the bone cache should not be in world space.
+ // Since the bone transforms are in world space, if we move or rotate the entity, its
+ // bones should be marked invalid.
+ //
+ // As it is, we're near ship, and don't have time to setup a good A/B test of how much
+ // overhead this fix would add. We've also only got one known case where the lack of
+ // this fix is screwing us, and I just fixed it, so I'm leaving this commented out for now.
+ //
+ // Hopefully, we'll put the bone cache in entity space and remove the need for this fix.
+ //
+ //#ifdef CLIENT_DLL
+ // if ( nChangeFlags & (POSITION_CHANGED | ANGLES_CHANGED | ANIMATION_CHANGED) )
+ // {
+ // C_BaseAnimating *pAnim = GetBaseAnimating();
+ // if ( pAnim )
+ // pAnim->InvalidateBoneCache();
+ // }
+ //#endif
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Returns the highest parent of an entity
+//-----------------------------------------------------------------------------
+CBaseEntity *CBaseEntity::GetRootMoveParent()
+{
+ CBaseEntity *pEntity = this;
+ CBaseEntity *pParent = this->GetMoveParent();
+ while ( pParent )
+ {
+ pEntity = pParent;
+ pParent = pEntity->GetMoveParent();
+ }
+
+ return pEntity;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: static method
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseEntity::IsPrecacheAllowed()
+{
+ return m_bAllowPrecache;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: static method
+// Input : allow -
+//-----------------------------------------------------------------------------
+void CBaseEntity::SetAllowPrecache( bool allow )
+{
+ m_bAllowPrecache = allow;
+}
+
+/*
+================
+FireBullets
+
+Go to the trouble of combining multiple pellets into a single damage call.
+================
+*/
+
+#if defined( GAME_DLL )
+class CBulletsTraceFilter : public CTraceFilterSimpleList
+{
+public:
+ CBulletsTraceFilter( int collisionGroup ) : CTraceFilterSimpleList( collisionGroup ) {}
+
+ bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
+ {
+ if ( m_PassEntities.Count() )
+ {
+ CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
+ CBaseEntity *pPassEntity = EntityFromEntityHandle( m_PassEntities[0] );
+ if ( pEntity && pPassEntity && pEntity->GetOwnerEntity() == pPassEntity &&
+ pPassEntity->IsSolidFlagSet(FSOLID_NOT_SOLID) && pPassEntity->IsSolidFlagSet( FSOLID_CUSTOMBOXTEST ) &&
+ pPassEntity->IsSolidFlagSet( FSOLID_CUSTOMRAYTEST ) )
+ {
+ // It's a bone follower of the entity to ignore (toml 8/3/2007)
+ return false;
+ }
+ }
+ return CTraceFilterSimpleList::ShouldHitEntity( pHandleEntity, contentsMask );
+ }
+
+};
+#else
+typedef CTraceFilterSimpleList CBulletsTraceFilter;
+#endif
+
+void CBaseEntity::FireBullets( const FireBulletsInfo_t &info )
+{
+ static int tracerCount;
+ trace_t tr;
+ CAmmoDef* pAmmoDef = GetAmmoDef();
+ int nDamageType = pAmmoDef->DamageType(info.m_iAmmoType);
+ int nAmmoFlags = pAmmoDef->Flags(info.m_iAmmoType);
+
+ bool bDoServerEffects = true;
+
+#if defined( HL2MP ) && defined( GAME_DLL )
+ bDoServerEffects = false;
+#endif
+
+#if defined( GAME_DLL )
+ if( IsPlayer() )
+ {
+ CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>(this);
+
+ int rumbleEffect = pPlayer->GetActiveWeapon()->GetRumbleEffect();
+
+ if( rumbleEffect != RUMBLE_INVALID )
+ {
+ if( rumbleEffect == RUMBLE_SHOTGUN_SINGLE )
+ {
+ if( info.m_iShots == 12 )
+ {
+ // Upgrade to double barrel rumble effect
+ rumbleEffect = RUMBLE_SHOTGUN_DOUBLE;
+ }
+ }
+
+ pPlayer->RumbleEffect( rumbleEffect, 0, RUMBLE_FLAG_RESTART );
+ }
+ }
+#endif// GAME_DLL
+
+ int iPlayerDamage = info.m_iPlayerDamage;
+ if ( iPlayerDamage == 0 )
+ {
+ if ( nAmmoFlags & AMMO_INTERPRET_PLRDAMAGE_AS_DAMAGE_TO_PLAYER )
+ {
+ iPlayerDamage = pAmmoDef->PlrDamage( info.m_iAmmoType );
+ }
+ }
+
+ // the default attacker is ourselves
+ CBaseEntity *pAttacker = info.m_pAttacker ? info.m_pAttacker : this;
+
+ // Make sure we don't have a dangling damage target from a recursive call
+ if ( g_MultiDamage.GetTarget() != NULL )
+ {
+ ApplyMultiDamage();
+ }
+
+ ClearMultiDamage();
+ g_MultiDamage.SetDamageType( nDamageType | DMG_NEVERGIB );
+
+ Vector vecDir;
+ Vector vecEnd;
+
+ // Skip multiple entities when tracing
+ CBulletsTraceFilter traceFilter( COLLISION_GROUP_NONE );
+ traceFilter.SetPassEntity( this ); // Standard pass entity for THIS so that it can be easily removed from the list after passing through a portal
+ traceFilter.AddEntityToIgnore( info.m_pAdditionalIgnoreEnt );
+
+#if defined( HL2_EPISODIC ) && defined( GAME_DLL )
+ // FIXME: We need to emulate this same behavior on the client as well -- jdw
+ // Also ignore a vehicle we're a passenger in
+ if ( MyCombatCharacterPointer() != NULL && MyCombatCharacterPointer()->IsInAVehicle() )
+ {
+ traceFilter.AddEntityToIgnore( MyCombatCharacterPointer()->GetVehicleEntity() );
+ }
+#endif // SERVER_DLL
+
+ bool bUnderwaterBullets = ShouldDrawUnderwaterBulletBubbles();
+ bool bStartedInWater = false;
+ if ( bUnderwaterBullets )
+ {
+ bStartedInWater = ( enginetrace->GetPointContents( info.m_vecSrc ) & (CONTENTS_WATER|CONTENTS_SLIME) ) != 0;
+ }
+
+ // Prediction is only usable on players
+ int iSeed = 0;
+ if ( IsPlayer() )
+ {
+ iSeed = CBaseEntity::GetPredictionRandomSeed() & 255;
+ }
+
+#if defined( HL2MP ) && defined( GAME_DLL )
+ int iEffectSeed = iSeed;
+#endif
+ //-----------------------------------------------------
+ // Set up our shot manipulator.
+ //-----------------------------------------------------
+ CShotManipulator Manipulator( info.m_vecDirShooting );
+
+ bool bDoImpacts = false;
+ bool bDoTracers = false;
+
+ float flCumulativeDamage = 0.0f;
+
+ for (int iShot = 0; iShot < info.m_iShots; iShot++)
+ {
+ bool bHitWater = false;
+ bool bHitGlass = false;
+
+ // Prediction is only usable on players
+ if ( IsPlayer() )
+ {
+ RandomSeed( iSeed ); // init random system with this seed
+ }
+
+ // If we're firing multiple shots, and the first shot has to be bang on target, ignore spread
+ if ( iShot == 0 && info.m_iShots > 1 && (info.m_nFlags & FIRE_BULLETS_FIRST_SHOT_ACCURATE) )
+ {
+ vecDir = Manipulator.GetShotDirection();
+ }
+ else
+ {
+
+ // Don't run the biasing code for the player at the moment.
+ vecDir = Manipulator.ApplySpread( info.m_vecSpread );
+ }
+
+ vecEnd = info.m_vecSrc + vecDir * info.m_flDistance;
+
+#ifdef PORTAL
+ CProp_Portal *pShootThroughPortal = NULL;
+ float fPortalFraction = 2.0f;
+#endif
+
+
+ if( IsPlayer() && info.m_iShots > 1 && iShot % 2 )
+ {
+ // Half of the shotgun pellets are hulls that make it easier to hit targets with the shotgun.
+#ifdef PORTAL
+ Ray_t rayBullet;
+ rayBullet.Init( info.m_vecSrc, vecEnd );
+ pShootThroughPortal = UTIL_Portal_FirstAlongRay( rayBullet, fPortalFraction );
+ if ( !UTIL_Portal_TraceRay_Bullets( pShootThroughPortal, rayBullet, MASK_SHOT, &traceFilter, &tr ) )
+ {
+ pShootThroughPortal = NULL;
+ }
+#else
+ AI_TraceHull( info.m_vecSrc, vecEnd, Vector( -3, -3, -3 ), Vector( 3, 3, 3 ), MASK_SHOT, &traceFilter, &tr );
+#endif //#ifdef PORTAL
+ }
+ else
+ {
+#ifdef PORTAL
+ Ray_t rayBullet;
+ rayBullet.Init( info.m_vecSrc, vecEnd );
+ pShootThroughPortal = UTIL_Portal_FirstAlongRay( rayBullet, fPortalFraction );
+ if ( !UTIL_Portal_TraceRay_Bullets( pShootThroughPortal, rayBullet, MASK_SHOT, &traceFilter, &tr ) )
+ {
+ pShootThroughPortal = NULL;
+ }
+#elif TF_DLL
+ CTraceFilterIgnoreFriendlyCombatItems traceFilterCombatItem( this, COLLISION_GROUP_NONE, GetTeamNumber() );
+ if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() )
+ {
+ CTraceFilterChain traceFilterChain( &traceFilter, &traceFilterCombatItem );
+ AI_TraceLine(info.m_vecSrc, vecEnd, MASK_SHOT, &traceFilterChain, &tr);
+ }
+ else
+ {
+ AI_TraceLine(info.m_vecSrc, vecEnd, MASK_SHOT, &traceFilter, &tr);
+ }
+#else
+ AI_TraceLine(info.m_vecSrc, vecEnd, MASK_SHOT, &traceFilter, &tr);
+#endif //#ifdef PORTAL
+ }
+
+ // Tracker 70354/63250: ywb 8/2/07
+ // Fixes bug where trace from turret with attachment point outside of Vcollide
+ // starts solid so doesn't hit anything else in the world and the final coord
+ // is outside of the MAX_COORD_FLOAT range. This cause trying to send the end pos
+ // of the tracer down to the client with an origin which is out-of-range for networking
+ if ( tr.startsolid )
+ {
+ tr.endpos = tr.startpos;
+ tr.fraction = 0.0f;
+ }
+
+ // bullet's final direction can be changed by passing through a portal
+#ifdef PORTAL
+ if ( !tr.startsolid )
+ {
+ vecDir = tr.endpos - tr.startpos;
+ VectorNormalize( vecDir );
+ }
+#endif
+
+#ifdef GAME_DLL
+ if ( ai_debug_shoot_positions.GetBool() )
+ NDebugOverlay::Line(info.m_vecSrc, vecEnd, 255, 255, 255, false, .1 );
+#endif
+
+ if ( bStartedInWater )
+ {
+#ifdef GAME_DLL
+ Vector vBubbleStart = info.m_vecSrc;
+ Vector vBubbleEnd = tr.endpos;
+
+#ifdef PORTAL
+ if ( pShootThroughPortal )
+ {
+ vBubbleEnd = info.m_vecSrc + ( vecEnd - info.m_vecSrc ) * fPortalFraction;
+ }
+#endif //#ifdef PORTAL
+
+ CreateBubbleTrailTracer( vBubbleStart, vBubbleEnd, vecDir );
+
+#ifdef PORTAL
+ if ( pShootThroughPortal )
+ {
+ Vector vTransformedIntersection;
+ UTIL_Portal_PointTransform( pShootThroughPortal->MatrixThisToLinked(), vBubbleEnd, vTransformedIntersection );
+
+ CreateBubbleTrailTracer( vTransformedIntersection, tr.endpos, vecDir );
+ }
+#endif //#ifdef PORTAL
+
+#endif //#ifdef GAME_DLL
+ bHitWater = true;
+ }
+
+ // Now hit all triggers along the ray that respond to shots...
+ // Clip the ray to the first collided solid returned from traceline
+ CTakeDamageInfo triggerInfo( pAttacker, pAttacker, info.m_flDamage, nDamageType );
+ CalculateBulletDamageForce( &triggerInfo, info.m_iAmmoType, vecDir, tr.endpos );
+ triggerInfo.ScaleDamageForce( info.m_flDamageForceScale );
+ triggerInfo.SetAmmoType( info.m_iAmmoType );
+#ifdef GAME_DLL
+ TraceAttackToTriggers( triggerInfo, tr.startpos, tr.endpos, vecDir );
+#endif
+
+ // Make sure given a valid bullet type
+ if (info.m_iAmmoType == -1)
+ {
+ DevMsg("ERROR: Undefined ammo type!\n");
+ return;
+ }
+
+ Vector vecTracerDest = tr.endpos;
+
+ // do damage, paint decals
+ if (tr.fraction != 1.0)
+ {
+#ifdef GAME_DLL
+ UpdateShotStatistics( tr );
+
+ // For shots that don't need persistance
+ int soundEntChannel = ( info.m_nFlags&FIRE_BULLETS_TEMPORARY_DANGER_SOUND ) ? SOUNDENT_CHANNEL_BULLET_IMPACT : SOUNDENT_CHANNEL_UNSPECIFIED;
+
+ CSoundEnt::InsertSound( SOUND_BULLET_IMPACT, tr.endpos, 200, 0.5, this, soundEntChannel );
+#endif
+
+ // See if the bullet ended up underwater + started out of the water
+ if ( !bHitWater && ( enginetrace->GetPointContents( tr.endpos ) & (CONTENTS_WATER|CONTENTS_SLIME) ) )
+ {
+ bHitWater = HandleShotImpactingWater( info, vecEnd, &traceFilter, &vecTracerDest );
+ }
+
+ float flActualDamage = info.m_flDamage;
+
+ // If we hit a player, and we have player damage specified, use that instead
+ // Adrian: Make sure to use the currect value if we hit a vehicle the player is currently driving.
+ if ( iPlayerDamage )
+ {
+ if ( tr.m_pEnt->IsPlayer() )
+ {
+ flActualDamage = iPlayerDamage;
+ }
+#ifdef GAME_DLL
+ else if ( tr.m_pEnt->GetServerVehicle() )
+ {
+ if ( tr.m_pEnt->GetServerVehicle()->GetPassenger() && tr.m_pEnt->GetServerVehicle()->GetPassenger()->IsPlayer() )
+ {
+ flActualDamage = iPlayerDamage;
+ }
+ }
+#endif
+ }
+
+ int nActualDamageType = nDamageType;
+ if ( flActualDamage == 0.0 )
+ {
+ flActualDamage = g_pGameRules->GetAmmoDamage( pAttacker, tr.m_pEnt, info.m_iAmmoType );
+ }
+ else
+ {
+ nActualDamageType = nDamageType | ((flActualDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB );
+ }
+
+ if ( !bHitWater || ((info.m_nFlags & FIRE_BULLETS_DONT_HIT_UNDERWATER) == 0) )
+ {
+ // Damage specified by function parameter
+ CTakeDamageInfo dmgInfo( this, pAttacker, flActualDamage, nActualDamageType );
+ ModifyFireBulletsDamage( &dmgInfo );
+ CalculateBulletDamageForce( &dmgInfo, info.m_iAmmoType, vecDir, tr.endpos );
+ dmgInfo.ScaleDamageForce( info.m_flDamageForceScale );
+ dmgInfo.SetAmmoType( info.m_iAmmoType );
+ tr.m_pEnt->DispatchTraceAttack( dmgInfo, vecDir, &tr );
+
+ if ( ToBaseCombatCharacter( tr.m_pEnt ) )
+ {
+ flCumulativeDamage += dmgInfo.GetDamage();
+ }
+
+ if ( bStartedInWater || !bHitWater || (info.m_nFlags & FIRE_BULLETS_ALLOW_WATER_SURFACE_IMPACTS) )
+ {
+ if ( bDoServerEffects == true )
+ {
+ DoImpactEffect( tr, nDamageType );
+ }
+ else
+ {
+ bDoImpacts = true;
+ }
+ }
+ else
+ {
+ // We may not impact, but we DO need to affect ragdolls on the client
+ CEffectData data;
+ data.m_vStart = tr.startpos;
+ data.m_vOrigin = tr.endpos;
+ data.m_nDamageType = nDamageType;
+
+ DispatchEffect( "RagdollImpact", data );
+ }
+
+#ifdef GAME_DLL
+ if ( nAmmoFlags & AMMO_FORCE_DROP_IF_CARRIED )
+ {
+ // Make sure if the player is holding this, he drops it
+ Pickup_ForcePlayerToDropThisObject( tr.m_pEnt );
+ }
+#endif
+ }
+ }
+
+ // See if we hit glass
+ if ( tr.m_pEnt != NULL )
+ {
+#ifdef GAME_DLL
+ surfacedata_t *psurf = physprops->GetSurfaceData( tr.surface.surfaceProps );
+ if ( ( psurf != NULL ) && ( psurf->game.material == CHAR_TEX_GLASS ) && ( tr.m_pEnt->ClassMatches( "func_breakable" ) ) )
+ {
+ // Query the func_breakable for whether it wants to allow for bullet penetration
+ if ( tr.m_pEnt->HasSpawnFlags( SF_BREAK_NO_BULLET_PENETRATION ) == false )
+ {
+ bHitGlass = true;
+ }
+ }
+#endif
+ }
+
+ if ( ( info.m_iTracerFreq != 0 ) && ( tracerCount++ % info.m_iTracerFreq ) == 0 && ( bHitGlass == false ) )
+ {
+ if ( bDoServerEffects == true )
+ {
+ Vector vecTracerSrc = vec3_origin;
+ ComputeTracerStartPosition( info.m_vecSrc, &vecTracerSrc );
+
+ trace_t Tracer;
+ Tracer = tr;
+ Tracer.endpos = vecTracerDest;
+
+#ifdef PORTAL
+ if ( pShootThroughPortal )
+ {
+ Tracer.endpos = info.m_vecSrc + ( vecEnd - info.m_vecSrc ) * fPortalFraction;
+ }
+#endif //#ifdef PORTAL
+
+ MakeTracer( vecTracerSrc, Tracer, pAmmoDef->TracerType(info.m_iAmmoType) );
+
+#ifdef PORTAL
+ if ( pShootThroughPortal )
+ {
+ Vector vTransformedIntersection;
+ UTIL_Portal_PointTransform( pShootThroughPortal->MatrixThisToLinked(), Tracer.endpos, vTransformedIntersection );
+ ComputeTracerStartPosition( vTransformedIntersection, &vecTracerSrc );
+
+ Tracer.endpos = vecTracerDest;
+
+ MakeTracer( vecTracerSrc, Tracer, pAmmoDef->TracerType(info.m_iAmmoType) );
+
+ // Shooting through a portal, the damage direction is translated through the passed-through portal
+ // so the damage indicator hud animation is correct
+ Vector vDmgOriginThroughPortal;
+ UTIL_Portal_PointTransform( pShootThroughPortal->MatrixThisToLinked(), info.m_vecSrc, vDmgOriginThroughPortal );
+ g_MultiDamage.SetDamagePosition ( vDmgOriginThroughPortal );
+ }
+ else
+ {
+ g_MultiDamage.SetDamagePosition ( info.m_vecSrc );
+ }
+#endif //#ifdef PORTAL
+ }
+ else
+ {
+ bDoTracers = true;
+ }
+ }
+
+ //NOTENOTE: We could expand this to a more general solution for various material penetration types (wood, thin metal, etc)
+
+ // See if we should pass through glass
+#ifdef GAME_DLL
+ if ( bHitGlass )
+ {
+ HandleShotImpactingGlass( info, tr, vecDir, &traceFilter );
+ }
+#endif
+
+ iSeed++;
+ }
+
+#if defined( HL2MP ) && defined( GAME_DLL )
+ if ( bDoServerEffects == false )
+ {
+ TE_HL2MPFireBullets( entindex(), tr.startpos, info.m_vecDirShooting, info.m_iAmmoType, iEffectSeed, info.m_iShots, info.m_vecSpread.x, bDoTracers, bDoImpacts );
+ }
+#endif
+
+#ifdef GAME_DLL
+ ApplyMultiDamage();
+
+ if ( IsPlayer() && flCumulativeDamage > 0.0f )
+ {
+ CBasePlayer *pPlayer = static_cast< CBasePlayer * >( this );
+ CTakeDamageInfo dmgInfo( this, pAttacker, flCumulativeDamage, nDamageType );
+ gamestats->Event_WeaponHit( pPlayer, info.m_bPrimaryAttack, pPlayer->GetActiveWeapon()->GetClassname(), dmgInfo );
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Should we draw bubbles underwater?
+//-----------------------------------------------------------------------------
+bool CBaseEntity::ShouldDrawUnderwaterBulletBubbles()
+{
+#if defined( HL2_DLL ) && defined( GAME_DLL )
+ CBaseEntity *pPlayer = ( gpGlobals->maxClients == 1 ) ? UTIL_GetLocalPlayer() : NULL;
+ return pPlayer && (pPlayer->GetWaterLevel() == 3);
+#else
+ return false;
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Handle shot entering water
+//-----------------------------------------------------------------------------
+bool CBaseEntity::HandleShotImpactingWater( const FireBulletsInfo_t &info,
+ const Vector &vecEnd, ITraceFilter *pTraceFilter, Vector *pVecTracerDest )
+{
+ trace_t waterTrace;
+
+ // Trace again with water enabled
+ AI_TraceLine( info.m_vecSrc, vecEnd, (MASK_SHOT|CONTENTS_WATER|CONTENTS_SLIME), pTraceFilter, &waterTrace );
+
+ // See if this is the point we entered
+ if ( ( enginetrace->GetPointContents( waterTrace.endpos - Vector(0,0,0.1f) ) & (CONTENTS_WATER|CONTENTS_SLIME) ) == 0 )
+ return false;
+
+ if ( ShouldDrawWaterImpacts() )
+ {
+ int nMinSplashSize = GetAmmoDef()->MinSplashSize(info.m_iAmmoType);
+ int nMaxSplashSize = GetAmmoDef()->MaxSplashSize(info.m_iAmmoType);
+
+ CEffectData data;
+ data.m_vOrigin = waterTrace.endpos;
+ data.m_vNormal = waterTrace.plane.normal;
+ data.m_flScale = random->RandomFloat( nMinSplashSize, nMaxSplashSize );
+ if ( waterTrace.contents & CONTENTS_SLIME )
+ {
+ data.m_fFlags |= FX_WATER_IN_SLIME;
+ }
+ DispatchEffect( "gunshotsplash", data );
+ }
+
+#ifdef GAME_DLL
+ if ( ShouldDrawUnderwaterBulletBubbles() )
+ {
+ CWaterBullet *pWaterBullet = ( CWaterBullet * )CreateEntityByName( "waterbullet" );
+ if ( pWaterBullet )
+ {
+ pWaterBullet->Spawn( waterTrace.endpos, info.m_vecDirShooting );
+
+ CEffectData tracerData;
+ tracerData.m_vStart = waterTrace.endpos;
+ tracerData.m_vOrigin = waterTrace.endpos + info.m_vecDirShooting * 400.0f;
+ tracerData.m_fFlags = TRACER_TYPE_WATERBULLET;
+ DispatchEffect( "TracerSound", tracerData );
+ }
+ }
+#endif
+
+ *pVecTracerDest = waterTrace.endpos;
+ return true;
+}
+
+
+ITraceFilter* CBaseEntity::GetBeamTraceFilter( void )
+{
+ return NULL;
+}
+
+void CBaseEntity::DispatchTraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
+{
+#ifdef GAME_DLL
+ // Make sure our damage filter allows the damage.
+ if ( !PassesDamageFilter( info ))
+ {
+ return;
+ }
+#endif
+
+ TraceAttack( info, vecDir, ptr, pAccumulator );
+}
+
+void CBaseEntity::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
+{
+ Vector vecOrigin = ptr->endpos - vecDir * 4;
+
+ if ( m_takedamage )
+ {
+#ifdef GAME_DLL
+ if ( pAccumulator )
+ {
+ pAccumulator->AccumulateMultiDamage( info, this );
+ }
+ else
+#endif // GAME_DLL
+ {
+ AddMultiDamage( info, this );
+ }
+
+ int blood = BloodColor();
+
+ if ( blood != DONT_BLEED )
+ {
+ SpawnBlood( vecOrigin, vecDir, blood, info.GetDamage() );// a little surface blood.
+ TraceBleed( info.GetDamage(), vecDir, ptr, info.GetDamageType() );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Allows the shooter to change the impact effect of his bullets
+//-----------------------------------------------------------------------------
+void CBaseEntity::DoImpactEffect( trace_t &tr, int nDamageType )
+{
+ // give shooter a chance to do a custom impact.
+ UTIL_ImpactTrace( &tr, nDamageType );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the tracer start position
+//-----------------------------------------------------------------------------
+void CBaseEntity::ComputeTracerStartPosition( const Vector &vecShotSrc, Vector *pVecTracerStart )
+{
+#ifndef HL2MP
+ if ( g_pGameRules->IsMultiplayer() )
+ {
+ // NOTE: we do this because in MakeTracer, we force it to use the attachment position
+ // in multiplayer, so the results from this function should never actually get used.
+ pVecTracerStart->Init( 999, 999, 999 );
+ return;
+ }
+#endif
+
+ if ( IsPlayer() )
+ {
+ // adjust tracer position for player
+ Vector forward, right;
+ CBasePlayer *pPlayer = ToBasePlayer( this );
+ pPlayer->EyeVectors( &forward, &right, NULL );
+ *pVecTracerStart = vecShotSrc + Vector ( 0 , 0 , -4 ) + right * 2 + forward * 16;
+ }
+ else
+ {
+ *pVecTracerStart = vecShotSrc;
+
+ CBaseCombatCharacter *pBCC = MyCombatCharacterPointer();
+ if ( pBCC != NULL )
+ {
+ CBaseCombatWeapon *pWeapon = pBCC->GetActiveWeapon();
+
+ if ( pWeapon != NULL )
+ {
+ Vector vecMuzzle;
+ QAngle vecMuzzleAngles;
+
+ if ( pWeapon->GetAttachment( 1, vecMuzzle, vecMuzzleAngles ) )
+ {
+ *pVecTracerStart = vecMuzzle;
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Virtual function allows entities to handle tracer presentation
+// as they see fit.
+//
+// Input : vecTracerSrc - the point at which to start the tracer (not always the
+// same spot as the traceline!
+//
+// tr - the entire trace result for the shot.
+//
+// Output :
+//-----------------------------------------------------------------------------
+void CBaseEntity::MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType )
+{
+ const char *pszTracerName = GetTracerType();
+
+ Vector vNewSrc = vecTracerSrc;
+
+ int iAttachment = GetTracerAttachment();
+
+ switch ( iTracerType )
+ {
+ case TRACER_LINE:
+ UTIL_Tracer( vNewSrc, tr.endpos, entindex(), iAttachment, 0.0f, false, pszTracerName );
+ break;
+
+ case TRACER_LINE_AND_WHIZ:
+ UTIL_Tracer( vNewSrc, tr.endpos, entindex(), iAttachment, 0.0f, true, pszTracerName );
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Default tracer attachment
+//-----------------------------------------------------------------------------
+int CBaseEntity::GetTracerAttachment( void )
+{
+ int iAttachment = TRACER_DONT_USE_ATTACHMENT;
+
+ if ( g_pGameRules->IsMultiplayer() )
+ {
+ iAttachment = 1;
+ }
+
+ return iAttachment;
+}
+
+
+int CBaseEntity::BloodColor()
+{
+ return DONT_BLEED;
+}
+
+
+void CBaseEntity::TraceBleed( float flDamage, const Vector &vecDir, trace_t *ptr, int bitsDamageType )
+{
+ if ((BloodColor() == DONT_BLEED) || (BloodColor() == BLOOD_COLOR_MECH))
+ {
+ return;
+ }
+
+ if (flDamage == 0)
+ return;
+
+ if (! (bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_AIRBOAT)))
+ return;
+
+ // make blood decal on the wall!
+ trace_t Bloodtr;
+ Vector vecTraceDir;
+ float flNoise;
+ int cCount;
+ int i;
+
+#ifdef GAME_DLL
+ if ( !IsAlive() )
+ {
+ // dealing with a dead npc.
+ if ( GetMaxHealth() <= 0 )
+ {
+ // no blood decal for a npc that has already decalled its limit.
+ return;
+ }
+ else
+ {
+ m_iMaxHealth -= 1;
+ }
+ }
+#endif
+
+ if (flDamage < 10)
+ {
+ flNoise = 0.1;
+ cCount = 1;
+ }
+ else if (flDamage < 25)
+ {
+ flNoise = 0.2;
+ cCount = 2;
+ }
+ else
+ {
+ flNoise = 0.3;
+ cCount = 4;
+ }
+
+ float flTraceDist = (bitsDamageType & DMG_AIRBOAT) ? 384 : 172;
+ for ( i = 0 ; i < cCount ; i++ )
+ {
+ vecTraceDir = vecDir * -1;// trace in the opposite direction the shot came from (the direction the shot is going)
+
+ vecTraceDir.x += random->RandomFloat( -flNoise, flNoise );
+ vecTraceDir.y += random->RandomFloat( -flNoise, flNoise );
+ vecTraceDir.z += random->RandomFloat( -flNoise, flNoise );
+
+ // Don't bleed on grates.
+ AI_TraceLine( ptr->endpos, ptr->endpos + vecTraceDir * -flTraceDist, MASK_SOLID_BRUSHONLY & ~CONTENTS_GRATE, this, COLLISION_GROUP_NONE, &Bloodtr);
+
+ if ( Bloodtr.fraction != 1.0 )
+ {
+ UTIL_BloodDecalTrace( &Bloodtr, BloodColor() );
+ }
+ }
+}
+
+
+const char* CBaseEntity::GetTracerType()
+{
+ return NULL;
+}
+
+void CBaseEntity::ModifyEmitSoundParams( EmitSound_t &params )
+{
+#ifdef CLIENT_DLL
+ if ( GameRules() )
+ {
+ params.m_pSoundName = GameRules()->TranslateEffectForVisionFilter( "sounds", params.m_pSoundName );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// These methods encapsulate MOVETYPE_FOLLOW, which became obsolete
+//-----------------------------------------------------------------------------
+void CBaseEntity::FollowEntity( CBaseEntity *pBaseEntity, bool bBoneMerge )
+{
+ if (pBaseEntity)
+ {
+ SetParent( pBaseEntity );
+ SetMoveType( MOVETYPE_NONE );
+
+ if ( bBoneMerge )
+ AddEffects( EF_BONEMERGE );
+
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ SetLocalOrigin( vec3_origin );
+ SetLocalAngles( vec3_angle );
+ }
+ else
+ {
+ StopFollowingEntity();
+ }
+}
+
+void CBaseEntity::SetEffectEntity( CBaseEntity *pEffectEnt )
+{
+ if ( m_hEffectEntity.Get() != pEffectEnt )
+ {
+ m_hEffectEntity = pEffectEnt;
+ }
+}
+
+void CBaseEntity::ApplyLocalVelocityImpulse( const Vector &inVecImpulse )
+{
+ // NOTE: Don't have to use GetVelocity here because local values
+ // are always guaranteed to be correct, unlike abs values which may
+ // require recomputation
+ if ( inVecImpulse != vec3_origin )
+ {
+ Vector vecImpulse = inVecImpulse;
+
+ // Safety check against receive a huge impulse, which can explode physics
+ switch ( CheckEntityVelocity( vecImpulse ) )
+ {
+ case -1:
+ Warning( "Discarding ApplyLocalVelocityImpulse(%f,%f,%f) on %s\n", vecImpulse.x, vecImpulse.y, vecImpulse.z, GetDebugName() );
+ Assert( false );
+ return;
+ case 0:
+ if ( CheckEmitReasonablePhysicsSpew() )
+ {
+ Warning( "Clamping ApplyLocalVelocityImpulse(%f,%f,%f) on %s\n", inVecImpulse.x, inVecImpulse.y, inVecImpulse.z, GetDebugName() );
+ }
+ break;
+ }
+
+ if ( GetMoveType() == MOVETYPE_VPHYSICS )
+ {
+ Vector worldVel;
+ VPhysicsGetObject()->LocalToWorld( &worldVel, vecImpulse );
+ VPhysicsGetObject()->AddVelocity( &worldVel, NULL );
+ }
+ else
+ {
+ InvalidatePhysicsRecursive( VELOCITY_CHANGED );
+ m_vecVelocity += vecImpulse;
+ }
+ }
+}
+
+void CBaseEntity::ApplyAbsVelocityImpulse( const Vector &inVecImpulse )
+{
+ if ( inVecImpulse != vec3_origin )
+ {
+ Vector vecImpulse = inVecImpulse;
+
+ // Safety check against receive a huge impulse, which can explode physics
+ switch ( CheckEntityVelocity( vecImpulse ) )
+ {
+ case -1:
+ Warning( "Discarding ApplyAbsVelocityImpulse(%f,%f,%f) on %s\n", vecImpulse.x, vecImpulse.y, vecImpulse.z, GetDebugName() );
+ Assert( false );
+ return;
+ case 0:
+ if ( CheckEmitReasonablePhysicsSpew() )
+ {
+ Warning( "Clamping ApplyAbsVelocityImpulse(%f,%f,%f) on %s\n", inVecImpulse.x, inVecImpulse.y, inVecImpulse.z, GetDebugName() );
+ }
+ break;
+ }
+
+ if ( GetMoveType() == MOVETYPE_VPHYSICS )
+ {
+ VPhysicsGetObject()->AddVelocity( &vecImpulse, NULL );
+ }
+ else
+ {
+ // NOTE: Have to use GetAbsVelocity here to ensure it's the correct value
+ Vector vecResult;
+ VectorAdd( GetAbsVelocity(), vecImpulse, vecResult );
+ SetAbsVelocity( vecResult );
+ }
+ }
+}
+
+void CBaseEntity::ApplyLocalAngularVelocityImpulse( const AngularImpulse &angImpulse )
+{
+ if (angImpulse != vec3_origin )
+ {
+ // Safety check against receive a huge impulse, which can explode physics
+ if ( !IsEntityAngularVelocityReasonable( angImpulse ) )
+ {
+ Warning( "Bad ApplyLocalAngularVelocityImpulse(%f,%f,%f) on %s\n", angImpulse.x, angImpulse.y, angImpulse.z, GetDebugName() );
+ Assert( false );
+ return;
+ }
+
+ if ( GetMoveType() == MOVETYPE_VPHYSICS )
+ {
+ VPhysicsGetObject()->AddVelocity( NULL, &angImpulse );
+ }
+ else
+ {
+ QAngle vecResult;
+ AngularImpulseToQAngle( angImpulse, vecResult );
+ VectorAdd( GetLocalAngularVelocity(), vecResult, vecResult );
+ SetLocalAngularVelocity( vecResult );
+ }
+ }
+}
+
+void CBaseEntity::SetCollisionGroup( int collisionGroup )
+{
+ if ( (int)m_CollisionGroup != collisionGroup )
+ {
+ m_CollisionGroup = collisionGroup;
+ CollisionRulesChanged();
+ }
+}
+
+
+void CBaseEntity::CollisionRulesChanged()
+{
+ // ivp maintains state based on recent return values from the collision filter, so anything
+ // that can change the state that a collision filter will return (like m_Solid) needs to call RecheckCollisionFilter.
+ if ( VPhysicsGetObject() )
+ {
+ extern bool PhysIsInCallback();
+ if ( PhysIsInCallback() )
+ {
+ Warning("Changing collision rules within a callback is likely to cause crashes!\n");
+ Assert(0);
+ }
+ IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ int count = VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
+ for ( int i = 0; i < count; i++ )
+ {
+ if ( pList[i] != NULL ) //this really shouldn't happen, but it does >_<
+ pList[i]->RecheckCollisionFilter();
+ }
+ }
+}
+
+int CBaseEntity::GetWaterType() const
+{
+ int out = 0;
+ if ( m_nWaterType & 1 )
+ out |= CONTENTS_WATER;
+ if ( m_nWaterType & 2 )
+ out |= CONTENTS_SLIME;
+ return out;
+}
+
+void CBaseEntity::SetWaterType( int nType )
+{
+ m_nWaterType = 0;
+ if ( nType & CONTENTS_WATER )
+ m_nWaterType |= 1;
+ if ( nType & CONTENTS_SLIME )
+ m_nWaterType |= 2;
+}
+
+ConVar sv_alternateticks( "sv_alternateticks", ( IsX360() ) ? "1" : "0", FCVAR_SPONLY, "If set, server only simulates entities on even numbered ticks.\n" );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseEntity::IsSimulatingOnAlternateTicks()
+{
+ if ( gpGlobals->maxClients != 1 )
+ {
+ return false;
+ }
+
+ return sv_alternateticks.GetBool();
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseEntity::IsToolRecording() const
+{
+#ifndef NO_TOOLFRAMEWORK
+ return m_bToolRecording;
+#else
+ return false;
+#endif
+}
+#endif