Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2847 lines
74 KiB

  1. //========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "decals.h"
  9. #include "effect_dispatch_data.h"
  10. #include "model_types.h"
  11. #include "gamestringpool.h"
  12. #include "ammodef.h"
  13. #include "takedamageinfo.h"
  14. #include "shot_manipulator.h"
  15. #include "ai_debug_shared.h"
  16. #include "mapentities_shared.h"
  17. #include "debugoverlay_shared.h"
  18. #include "coordsize.h"
  19. #include "vphysics/performance.h"
  20. #ifdef CLIENT_DLL
  21. #include "c_te_effect_dispatch.h"
  22. #else
  23. #include "te_effect_dispatch.h"
  24. #include "soundent.h"
  25. #include "iservervehicle.h"
  26. #include "player_pickup.h"
  27. #include "waterbullet.h"
  28. #include "func_break.h"
  29. #include "GameStats.h"
  30. #endif
  31. #ifdef HL2_EPISODIC
  32. ConVar hl2_episodic( "hl2_episodic", "1", FCVAR_REPLICATED );
  33. #else
  34. ConVar hl2_episodic( "hl2_episodic", "0", FCVAR_REPLICATED );
  35. #endif//HL2_EPISODIC
  36. #ifdef PORTAL
  37. #include "portal_base2d_shared.h"
  38. #endif
  39. #include "rumble_shared.h"
  40. // memdbgon must be the last include file in a .cpp file!!!
  41. #include "tier0/memdbgon.h"
  42. #ifdef GAME_DLL
  43. ConVar ent_debugkeys( "ent_debugkeys", "" );
  44. extern bool ParseKeyvalue( void *pObject, typedescription_t *pFields, int iNumFields, const char *szKeyName, const char *szValue );
  45. extern bool ExtractKeyvalue( void *pObject, typedescription_t *pFields, int iNumFields, const char *szKeyName, char *szValue, int iMaxLen );
  46. #endif
  47. bool CBaseEntity::m_bAllowPrecache = false;
  48. bool CBaseEntity::sm_bAccurateTriggerBboxChecks = true; // set to false for legacy behavior in ep1
  49. // Set default max values for entities based on the existing constants from elsewhere
  50. float k_flMaxEntityPosCoord = MAX_COORD_FLOAT;
  51. float k_flMaxEntityEulerAngle = 360.0 * 1000.0f; // really should be restricted to +/-180, but some code doesn't adhere to this. It gets wrapped eventually
  52. float k_flMaxEntitySpeed = k_flMaxVelocity;
  53. float k_flMaxEntitySpinRate = k_flMaxAngularVelocity;
  54. ConVar sv_clamp_unsafe_velocities( "sv_clamp_unsafe_velocities", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Whether the server will attempt to clamp velocities that could cause physics bugs or crashes." );
  55. ConVar ai_shot_bias_min( "ai_shot_bias_min", "-1.0", FCVAR_REPLICATED );
  56. ConVar ai_shot_bias_max( "ai_shot_bias_max", "1.0", FCVAR_REPLICATED );
  57. ConVar ai_debug_shoot_positions( "ai_debug_shoot_positions", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
  58. // Utility func to throttle rate at which the "reasonable position" spew goes out
  59. static double s_LastEntityReasonableEmitTime = -DBL_MAX;
  60. bool CheckEmitReasonablePhysicsSpew()
  61. {
  62. // Reported recently?
  63. double now = Plat_FloatTime();
  64. if ( now >= s_LastEntityReasonableEmitTime && now < s_LastEntityReasonableEmitTime + 5.0 )
  65. {
  66. // Already reported recently
  67. return false;
  68. }
  69. // Not reported recently. Report it now
  70. s_LastEntityReasonableEmitTime = now;
  71. return true;
  72. }
  73. DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_DEVELOPER_VERBOSE, "DeveloperVerbose" );
  74. //-----------------------------------------------------------------------------
  75. // Purpose: Spawn some blood particles
  76. //-----------------------------------------------------------------------------
  77. void SpawnBlood(Vector vecSpot, const Vector &vecDir, int bloodColor, float flDamage)
  78. {
  79. UTIL_BloodDrips( vecSpot, vecDir, bloodColor, (int)flDamage );
  80. }
  81. #if !defined( NO_ENTITY_PREDICTION )
  82. //-----------------------------------------------------------------------------
  83. // The player drives simulation of this entity
  84. //-----------------------------------------------------------------------------
  85. void CBaseEntity::SetPlayerSimulated( CBasePlayer *pOwner )
  86. {
  87. m_bIsPlayerSimulated = true;
  88. pOwner->AddToPlayerSimulationList( this );
  89. m_hPlayerSimulationOwner = pOwner;
  90. }
  91. void CBaseEntity::UnsetPlayerSimulated( void )
  92. {
  93. if ( m_hPlayerSimulationOwner != NULL )
  94. {
  95. m_hPlayerSimulationOwner->RemoveFromPlayerSimulationList( this );
  96. }
  97. m_hPlayerSimulationOwner = NULL;
  98. m_bIsPlayerSimulated = false;
  99. }
  100. #endif
  101. // position of eyes
  102. Vector CBaseEntity::EyePosition( void )
  103. {
  104. return GetAbsOrigin() + m_vecViewOffset;
  105. }
  106. const QAngle &CBaseEntity::EyeAngles( void )
  107. {
  108. return GetAbsAngles();
  109. }
  110. const QAngle &CBaseEntity::LocalEyeAngles( void )
  111. {
  112. return GetLocalAngles();
  113. }
  114. // position of ears
  115. Vector CBaseEntity::EarPosition( void )
  116. {
  117. return EyePosition( );
  118. }
  119. void CBaseEntity::SetViewOffset( const Vector& v )
  120. {
  121. m_vecViewOffset = v;
  122. }
  123. const Vector& CBaseEntity::GetViewOffset() const
  124. {
  125. return m_vecViewOffset;
  126. }
  127. //-----------------------------------------------------------------------------
  128. // center point of entity
  129. //-----------------------------------------------------------------------------
  130. const Vector &CBaseEntity::WorldSpaceCenter( ) const
  131. {
  132. return CollisionProp()->WorldSpaceCenter();
  133. }
  134. #if !defined( CLIENT_DLL )
  135. #define CHANGE_FLAGS(flags,newFlags) { unsigned int old = flags; flags = (newFlags); gEntList.ReportEntityFlagsChanged( this, old, flags ); }
  136. #else
  137. #define CHANGE_FLAGS(flags,newFlags) (flags = (newFlags))
  138. #endif
  139. void CBaseEntity::AddFlag( int flags )
  140. {
  141. CHANGE_FLAGS( m_fFlags, m_fFlags | flags );
  142. }
  143. void CBaseEntity::RemoveFlag( int flagsToRemove )
  144. {
  145. CHANGE_FLAGS( m_fFlags, m_fFlags & ~flagsToRemove );
  146. }
  147. void CBaseEntity::ClearFlags( void )
  148. {
  149. CHANGE_FLAGS( m_fFlags, 0 );
  150. }
  151. void CBaseEntity::ToggleFlag( int flagToToggle )
  152. {
  153. CHANGE_FLAGS( m_fFlags, m_fFlags ^ flagToToggle );
  154. }
  155. void CBaseEntity::SetEffects( int nEffects )
  156. {
  157. if ( nEffects != m_fEffects )
  158. {
  159. #if defined ( CLIENT_DLL )
  160. bool bFastReflectionChanged =
  161. ( (nEffects & EF_MARKED_FOR_FAST_REFLECTION ) != ( m_fEffects & EF_MARKED_FOR_FAST_REFLECTION ) );
  162. #endif
  163. #if !defined( CLIENT_DLL )
  164. #ifdef HL2_EPISODIC
  165. // Hack for now, to avoid player emitting radius with his flashlight
  166. if ( !IsPlayer() )
  167. {
  168. if ( (nEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) && !(m_fEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) )
  169. {
  170. AddEntityToDarknessCheck( this );
  171. }
  172. else if ( !(nEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) && (m_fEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) )
  173. {
  174. RemoveEntityFromDarknessCheck( this );
  175. }
  176. }
  177. #endif // HL2_EPISODIC
  178. #endif // !CLIENT_DLL
  179. m_fEffects = nEffects;
  180. #if !defined( CLIENT_DLL )
  181. if ( nEffects & ( EF_NOINTERP ) )
  182. {
  183. gEntList.AddPostClientMessageEntity( this );
  184. }
  185. #endif
  186. if ( ( nEffects & EF_NOINTERP ) && IsPlayer() )
  187. {
  188. ((CBasePlayer *)this)->IncrementEFNoInterpParity();
  189. }
  190. #ifndef CLIENT_DLL
  191. DispatchUpdateTransmitState();
  192. #else
  193. UpdateVisibility();
  194. if ( bFastReflectionChanged )
  195. {
  196. OnFastReflectionRenderingChanged();
  197. }
  198. OnDisableShadowDepthRenderingChanged();
  199. OnDisableCSMRenderingChanged();
  200. OnShadowDepthRenderingCacheableStateChanged();
  201. #endif
  202. }
  203. }
  204. void CBaseEntity::AddEffects( int nEffects )
  205. {
  206. #if !defined( CLIENT_DLL )
  207. #ifdef HL2_EPISODIC
  208. if ( (nEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) && !(m_fEffects & (EF_BRIGHTLIGHT|EF_DIMLIGHT)) )
  209. {
  210. // Hack for now, to avoid player emitting radius with his flashlight
  211. if ( !IsPlayer() )
  212. {
  213. AddEntityToDarknessCheck( this );
  214. }
  215. }
  216. #endif // HL2_EPISODIC
  217. #endif // !CLIENT_DLL
  218. m_fEffects |= nEffects;
  219. #if !defined( CLIENT_DLL )
  220. if ( nEffects & ( EF_NOINTERP ) )
  221. {
  222. gEntList.AddPostClientMessageEntity( this );
  223. }
  224. #else
  225. if ( m_fEffects & (EF_DIMLIGHT|EF_DIMLIGHT) )
  226. {
  227. AddToEntityList(ENTITY_LIST_PRERENDER);
  228. }
  229. #endif
  230. if ( nEffects & EF_NODRAW)
  231. {
  232. #ifndef CLIENT_DLL
  233. DispatchUpdateTransmitState();
  234. #else
  235. UpdateVisibility();
  236. #endif
  237. }
  238. #ifdef CLIENT_DLL
  239. if ( nEffects & EF_MARKED_FOR_FAST_REFLECTION )
  240. {
  241. OnFastReflectionRenderingChanged();
  242. }
  243. OnDisableShadowDepthRenderingChanged();
  244. OnShadowDepthRenderingCacheableStateChanged();
  245. #endif
  246. }
  247. void CBaseEntity::SetBlocksLOS( bool bBlocksLOS )
  248. {
  249. if ( bBlocksLOS )
  250. {
  251. RemoveEFlags( EFL_DONTBLOCKLOS );
  252. }
  253. else
  254. {
  255. AddEFlags( EFL_DONTBLOCKLOS );
  256. }
  257. }
  258. bool CBaseEntity::BlocksLOS( void )
  259. {
  260. return !IsEFlagSet(EFL_DONTBLOCKLOS);
  261. }
  262. void CBaseEntity::SetAIWalkable( bool bBlocksLOS )
  263. {
  264. if ( bBlocksLOS )
  265. {
  266. RemoveEFlags( EFL_DONTWALKON );
  267. }
  268. else
  269. {
  270. AddEFlags( EFL_DONTWALKON );
  271. }
  272. }
  273. bool CBaseEntity::IsAIWalkable( void )
  274. {
  275. return !IsEFlagSet(EFL_DONTWALKON);
  276. }
  277. //-----------------------------------------------------------------------------
  278. // Purpose: Handles keys and outputs from the BSP.
  279. // Input : mapData - Text block of keys and values from the BSP.
  280. //-----------------------------------------------------------------------------
  281. void CBaseEntity::ParseMapData( CEntityMapData *mapData )
  282. {
  283. char keyName[MAPKEY_MAXLENGTH];
  284. char value[MAPKEY_MAXLENGTH];
  285. #ifdef _DEBUG
  286. #ifdef GAME_DLL
  287. ValidateDataDescription();
  288. #endif // GAME_DLL
  289. #endif // _DEBUG
  290. // loop through all keys in the data block and pass the info back into the object
  291. if ( mapData->GetFirstKey(keyName, value) )
  292. {
  293. do
  294. {
  295. KeyValue( keyName, value );
  296. }
  297. while ( mapData->GetNextKey(keyName, value) );
  298. }
  299. OnParseMapDataFinished();
  300. }
  301. //-----------------------------------------------------------------------------
  302. // Parse data from a map file
  303. //-----------------------------------------------------------------------------
  304. bool CBaseEntity::KeyValue( const char *szKeyName, const char *szValue )
  305. {
  306. //!! temp hack, until worldcraft is fixed
  307. // strip the # tokens from (duplicate) key names
  308. char *s = (char *)strchr( szKeyName, '#' );
  309. if ( s )
  310. {
  311. *s = '\0';
  312. }
  313. if ( FStrEq( szKeyName, "rendercolor" ) || FStrEq( szKeyName, "rendercolor32" ))
  314. {
  315. color32 tmp;
  316. V_StringToColor32( &tmp, szValue );
  317. SetRenderColor( tmp.r, tmp.g, tmp.b );
  318. SetRenderAlpha( tmp.a );
  319. return true;
  320. }
  321. if ( FStrEq( szKeyName, "renderamt" ) )
  322. {
  323. SetRenderAlpha( Q_atoi( szValue ) );
  324. return true;
  325. }
  326. if ( FStrEq( szKeyName, "disableshadows" ))
  327. {
  328. int val = atoi( szValue );
  329. if (val)
  330. {
  331. AddEffects( EF_NOSHADOW );
  332. }
  333. return true;
  334. }
  335. if ( FStrEq( szKeyName, "drawinfastreflection" ) )
  336. {
  337. int val = atoi( szValue );
  338. if (val)
  339. {
  340. AddEffects( EF_MARKED_FOR_FAST_REFLECTION );
  341. }
  342. return true;
  343. }
  344. if ( FStrEq( szKeyName, "disableshadowdepth" ) )
  345. {
  346. int val = atoi( szValue );
  347. if (val)
  348. {
  349. AddEffects( EF_NOSHADOWDEPTH );
  350. }
  351. return true;
  352. }
  353. if ( FStrEq( szKeyName, "shadowdepthnocache" ) )
  354. {
  355. int val = atoi( szValue );
  356. if ( val == 1 )
  357. {
  358. AddEffects( EF_SHADOWDEPTH_NOCACHE );
  359. }
  360. else if ( val == 2 )
  361. {
  362. RemoveEffects( EF_SHADOWDEPTH_NOCACHE );
  363. }
  364. return true;
  365. }
  366. if ( FStrEq( szKeyName, "mins" ))
  367. {
  368. Vector mins;
  369. UTIL_StringToVector( mins.Base(), szValue );
  370. CollisionProp()->SetCollisionBounds( mins, CollisionProp()->OBBMaxs() );
  371. return true;
  372. }
  373. if ( FStrEq( szKeyName, "maxs" ))
  374. {
  375. Vector maxs;
  376. UTIL_StringToVector( maxs.Base(), szValue );
  377. CollisionProp()->SetCollisionBounds( CollisionProp()->OBBMins(), maxs );
  378. return true;
  379. }
  380. if ( FStrEq( szKeyName, "disablereceiveshadows" ))
  381. {
  382. int val = atoi( szValue );
  383. if (val)
  384. {
  385. AddEffects( EF_NORECEIVESHADOW );
  386. }
  387. return true;
  388. }
  389. if ( FStrEq( szKeyName, "disableflashlight" ))
  390. {
  391. int val = atoi( szValue );
  392. if (val)
  393. {
  394. AddEffects( EF_NOFLASHLIGHT );
  395. }
  396. return true;
  397. }
  398. if ( FStrEq( szKeyName, "nodamageforces" ))
  399. {
  400. int val = atoi( szValue );
  401. if (val)
  402. {
  403. AddEFlags( EFL_NO_DAMAGE_FORCES );
  404. }
  405. return true;
  406. }
  407. // Fix up single angles
  408. if( FStrEq( szKeyName, "angle" ) )
  409. {
  410. static char szBuf[64];
  411. float y = atof( szValue );
  412. if (y >= 0)
  413. {
  414. Q_snprintf( szBuf,sizeof(szBuf), "%f %f %f", GetLocalAngles()[0], y, GetLocalAngles()[2] );
  415. }
  416. else if ((int)y == -1)
  417. {
  418. Q_strncpy( szBuf, "-90 0 0", sizeof(szBuf) );
  419. }
  420. else
  421. {
  422. Q_strncpy( szBuf, "90 0 0", sizeof(szBuf) );
  423. }
  424. // Do this so inherited classes looking for 'angles' don't have to bother with 'angle'
  425. return KeyValue( szKeyName, szBuf );
  426. }
  427. // NOTE: Have to do these separate because they set two values instead of one
  428. if( FStrEq( szKeyName, "angles" ) )
  429. {
  430. QAngle angles;
  431. UTIL_StringToVector( angles.Base(), szValue );
  432. // If you're hitting this assert, it's probably because you're
  433. // calling SetLocalAngles from within a KeyValues method.. use SetAbsAngles instead!
  434. Assert( (GetMoveParent() == NULL) && !IsEFlagSet( EFL_DIRTY_ABSTRANSFORM ) );
  435. SetAbsAngles( angles );
  436. return true;
  437. }
  438. if( FStrEq( szKeyName, "origin" ) )
  439. {
  440. Vector vecOrigin;
  441. UTIL_StringToVector( vecOrigin.Base(), szValue );
  442. // If you're hitting this assert, it's probably because you're
  443. // calling SetLocalOrigin from within a KeyValues method.. use SetAbsOrigin instead!
  444. Assert( (GetMoveParent() == NULL) && !IsEFlagSet( EFL_DIRTY_ABSTRANSFORM ) );
  445. SetAbsOrigin( vecOrigin );
  446. return true;
  447. }
  448. #ifdef GAME_DLL
  449. if ( FStrEq( szKeyName, "targetname" ) )
  450. {
  451. SetName( AllocPooledString( szValue ) );
  452. return true;
  453. }
  454. // loop through the data description, and try and place the keys in
  455. if ( !*ent_debugkeys.GetString() )
  456. {
  457. for ( datamap_t *dmap = GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
  458. {
  459. if ( ::ParseKeyvalue(this, dmap->dataDesc, dmap->dataNumFields, szKeyName, szValue) )
  460. {
  461. //we don't know what changed, so mark us as fully invalid
  462. if( edict() )
  463. edict()->StateChanged();
  464. return true;
  465. }
  466. }
  467. }
  468. else
  469. {
  470. // debug version - can be used to see what keys have been parsed in
  471. bool printKeyHits = false;
  472. const char *debugName = "";
  473. if ( *ent_debugkeys.GetString() && !Q_stricmp(ent_debugkeys.GetString(), STRING(m_iClassname)) )
  474. {
  475. // Msg( "-- found entity of type %s\n", STRING(m_iClassname) );
  476. printKeyHits = true;
  477. debugName = STRING(m_iClassname);
  478. }
  479. // loop through the data description, and try and place the keys in
  480. for ( datamap_t *dmap = GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
  481. {
  482. if ( !printKeyHits && *ent_debugkeys.GetString() && !Q_stricmp(dmap->dataClassName, ent_debugkeys.GetString()) )
  483. {
  484. // Msg( "-- found class of type %s\n", dmap->dataClassName );
  485. printKeyHits = true;
  486. debugName = dmap->dataClassName;
  487. }
  488. if ( ::ParseKeyvalue(this, dmap->dataDesc, dmap->dataNumFields, szKeyName, szValue) )
  489. {
  490. //we don't know what changed, so mark us as fully invalid
  491. if( edict() )
  492. edict()->StateChanged();
  493. if ( printKeyHits )
  494. Msg( "(%s) key: %-16s value: %s\n", debugName, szKeyName, szValue );
  495. return true;
  496. }
  497. }
  498. if ( printKeyHits )
  499. Msg( "!! (%s) key not handled: \"%s\" \"%s\"\n", STRING(m_iClassname), szKeyName, szValue );
  500. }
  501. #else
  502. // HACK: Read dxlevels for client-side entities
  503. if ( FStrEq( szKeyName, "mincpulevel" ))
  504. {
  505. m_nMinCPULevel = atoi( szValue );
  506. return true;
  507. }
  508. if ( FStrEq( szKeyName, "maxcpulevel" ))
  509. {
  510. m_nMaxCPULevel = atoi( szValue );
  511. return true;
  512. }
  513. if ( FStrEq( szKeyName, "mingpulevel" ))
  514. {
  515. m_nMinGPULevel = atoi( szValue );
  516. return true;
  517. }
  518. if ( FStrEq( szKeyName, "maxgpulevel" ))
  519. {
  520. m_nMaxGPULevel = atoi( szValue );
  521. return true;
  522. }
  523. #endif
  524. // key hasn't been handled
  525. return false;
  526. }
  527. bool CBaseEntity::KeyValue( const char *szKeyName, float flValue )
  528. {
  529. char string[256];
  530. Q_snprintf(string,sizeof(string), "%f", flValue );
  531. return KeyValue( szKeyName, string );
  532. }
  533. bool CBaseEntity::KeyValue( const char *szKeyName, int nValue )
  534. {
  535. char string[256];
  536. Q_snprintf(string,sizeof(string), "%d", nValue );
  537. return KeyValue( szKeyName, string );
  538. }
  539. bool CBaseEntity::KeyValue( const char *szKeyName, const Vector &vecValue )
  540. {
  541. char string[256];
  542. Q_snprintf(string,sizeof(string), "%f %f %f", vecValue.x, vecValue.y, vecValue.z );
  543. return KeyValue( szKeyName, string );
  544. }
  545. //-----------------------------------------------------------------------------
  546. // Purpose:
  547. // Input :
  548. // Output :
  549. //-----------------------------------------------------------------------------
  550. bool CBaseEntity::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen )
  551. {
  552. if ( FStrEq( szKeyName, "rendercolor" ) || FStrEq( szKeyName, "rendercolor32" ))
  553. {
  554. color24 tmp = GetRenderColor();
  555. unsigned char a = GetRenderAlpha();
  556. Q_snprintf( szValue, iMaxLen, "%d %d %d %d", tmp.r, tmp.g, tmp.b, a );
  557. return true;
  558. }
  559. if ( FStrEq( szKeyName, "renderamt" ) )
  560. {
  561. unsigned char a = GetRenderAlpha();
  562. Q_snprintf( szValue, iMaxLen, "%d", a );
  563. return true;
  564. }
  565. if ( FStrEq( szKeyName, "disableshadows" ))
  566. {
  567. Q_snprintf( szValue, iMaxLen, "%d", IsEffectActive( EF_NOSHADOW ) );
  568. return true;
  569. }
  570. if ( FStrEq( szKeyName, "mins" ))
  571. {
  572. Assert( 0 );
  573. return false;
  574. }
  575. if ( FStrEq( szKeyName, "maxs" ))
  576. {
  577. Assert( 0 );
  578. return false;
  579. }
  580. if ( FStrEq( szKeyName, "disablereceiveshadows" ))
  581. {
  582. Q_snprintf( szValue, iMaxLen, "%d", IsEffectActive( EF_NORECEIVESHADOW ) );
  583. return true;
  584. }
  585. if ( FStrEq( szKeyName, "disableflashlight" ))
  586. {
  587. Q_snprintf( szValue, iMaxLen, "%d", IsEffectActive( EF_NOFLASHLIGHT ) );
  588. return true;
  589. }
  590. if ( FStrEq( szKeyName, "nodamageforces" ))
  591. {
  592. Q_snprintf( szValue, iMaxLen, "%d", IsEffectActive( EFL_NO_DAMAGE_FORCES ) );
  593. return true;
  594. }
  595. // Fix up single angles
  596. if( FStrEq( szKeyName, "angle" ) )
  597. {
  598. return false;
  599. }
  600. // NOTE: Have to do these separate because they set two values instead of one
  601. if( FStrEq( szKeyName, "angles" ) )
  602. {
  603. QAngle angles = GetAbsAngles();
  604. Q_snprintf( szValue, iMaxLen, "%f %f %f", angles.x, angles.y, angles.z );
  605. return true;
  606. }
  607. if( FStrEq( szKeyName, "origin" ) )
  608. {
  609. Vector vecOrigin = GetAbsOrigin();
  610. Q_snprintf( szValue, iMaxLen, "%f %f %f", vecOrigin.x, vecOrigin.y, vecOrigin.z );
  611. return true;
  612. }
  613. #ifdef GAME_DLL
  614. if ( FStrEq( szKeyName, "targetname" ) )
  615. {
  616. Q_snprintf( szValue, iMaxLen, "%s", STRING( GetEntityName() ) );
  617. return true;
  618. }
  619. if ( FStrEq( szKeyName, "classname" ) )
  620. {
  621. Q_snprintf( szValue, iMaxLen, "%s", GetClassname() );
  622. return true;
  623. }
  624. for ( datamap_t *dmap = GetDataDescMap(); dmap != NULL; dmap = dmap->baseMap )
  625. {
  626. if ( ::ExtractKeyvalue( this, dmap->dataDesc, dmap->dataNumFields, szKeyName, szValue, iMaxLen ) )
  627. return true;
  628. }
  629. #endif
  630. return false;
  631. }
  632. //-----------------------------------------------------------------------------
  633. // Purpose:
  634. // Input : collisionGroup -
  635. // Output : Returns true on success, false on failure.
  636. //-----------------------------------------------------------------------------
  637. bool CBaseEntity::ShouldCollide( int collisionGroup, int contentsMask ) const
  638. {
  639. if ( m_CollisionGroup == COLLISION_GROUP_DEBRIS )
  640. {
  641. if ( ! (contentsMask & CONTENTS_DEBRIS) )
  642. return false;
  643. }
  644. return true;
  645. }
  646. //-----------------------------------------------------------------------------
  647. // Purpose:
  648. // Input : seed -
  649. //-----------------------------------------------------------------------------
  650. void CBaseEntity::SetPredictionRandomSeed( const CUserCmd *cmd )
  651. {
  652. if ( !cmd )
  653. {
  654. m_nPredictionRandomSeed = -1;
  655. #ifndef CLIENT_DLL
  656. m_nPredictionRandomSeedServer = -1;
  657. #endif
  658. return;
  659. }
  660. m_nPredictionRandomSeed = ( cmd->random_seed );
  661. #ifndef CLIENT_DLL
  662. m_nPredictionRandomSeedServer = ( cmd->server_random_seed );
  663. #endif
  664. }
  665. //------------------------------------------------------------------------------
  666. // Purpose : Base implimentation for entity handling decals
  667. //------------------------------------------------------------------------------
  668. void CBaseEntity::DecalTrace( trace_t *pTrace, char const *decalName )
  669. {
  670. int index = decalsystem->GetDecalIndexForName( decalName );
  671. if ( index < 0 )
  672. return;
  673. Assert( pTrace->m_pEnt );
  674. CBroadcastRecipientFilter filter;
  675. te->Decal( filter, 0.0, &pTrace->endpos, &pTrace->startpos,
  676. pTrace->GetEntityIndex(), pTrace->hitbox, index );
  677. }
  678. //-----------------------------------------------------------------------------
  679. // Purpose: Base handling for impacts against entities
  680. //-----------------------------------------------------------------------------
  681. void CBaseEntity::ImpactTrace( trace_t *pTrace, int iDamageType, char *pCustomImpactName )
  682. {
  683. VPROF( "CBaseEntity::ImpactTrace" );
  684. Assert( pTrace->m_pEnt );
  685. CBaseEntity *pEntity = pTrace->m_pEnt;
  686. // Build the impact data
  687. CEffectData data;
  688. data.m_vOrigin = pTrace->endpos;
  689. data.m_vStart = pTrace->startpos;
  690. data.m_nSurfaceProp = pTrace->surface.surfaceProps;
  691. if ( data.m_nSurfaceProp < 0 )
  692. {
  693. data.m_nSurfaceProp = 0;
  694. }
  695. data.m_nDamageType = iDamageType;
  696. data.m_nHitBox = pTrace->hitbox;
  697. #ifdef CLIENT_DLL
  698. data.m_hEntity = ClientEntityList().EntIndexToHandle( pEntity->entindex() );
  699. #else
  700. data.m_nEntIndex = pEntity->entindex();
  701. #endif
  702. // Send it on its way
  703. if ( !pCustomImpactName )
  704. {
  705. DispatchEffect( "Impact", data );
  706. }
  707. else
  708. {
  709. DispatchEffect( pCustomImpactName, data );
  710. }
  711. }
  712. //-----------------------------------------------------------------------------
  713. // Purpose: returns the damage decal to use, given a damage type
  714. // Input : bitsDamageType - the damage type
  715. // Output : the index of the damage decal to use
  716. //-----------------------------------------------------------------------------
  717. char const *CBaseEntity::DamageDecal( int bitsDamageType, int gameMaterial )
  718. {
  719. if ( m_nRenderMode == kRenderTransAlpha )
  720. return "";
  721. if ( m_nRenderMode != kRenderNormal && gameMaterial == 'G' )
  722. return "BulletProof";
  723. if ( bitsDamageType == DMG_SLASH )
  724. return "ManhackCut";
  725. // This will get translated at a lower layer based on game material
  726. return "Impact.Concrete";
  727. }
  728. //-----------------------------------------------------------------------------
  729. // Purpose:
  730. //-----------------------------------------------------------------------------
  731. int CBaseEntity::GetIndexForThinkContext( const char *pszContext )
  732. {
  733. for ( int i = 0; i < m_aThinkFunctions.Count(); i++ )
  734. {
  735. if ( !Q_strncmp( STRING( m_aThinkFunctions[i].m_iszContext ), pszContext, MAX_CONTEXT_LENGTH ) )
  736. return i;
  737. }
  738. return NO_THINK_CONTEXT;
  739. }
  740. //-----------------------------------------------------------------------------
  741. // Purpose: Get a fresh think context for this entity
  742. //-----------------------------------------------------------------------------
  743. int CBaseEntity::RegisterThinkContext( const char *szContext )
  744. {
  745. int iIndex = GetIndexForThinkContext( szContext );
  746. if ( iIndex != NO_THINK_CONTEXT )
  747. return iIndex;
  748. // Make a new think func
  749. thinkfunc_t sNewFunc;
  750. Q_memset( &sNewFunc, 0, sizeof( sNewFunc ) );
  751. sNewFunc.m_pfnThink = NULL;
  752. sNewFunc.m_nNextThinkTick = 0;
  753. sNewFunc.m_iszContext = AllocPooledString(szContext);
  754. // Insert it into our list
  755. return m_aThinkFunctions.AddToTail( sNewFunc );
  756. }
  757. //-----------------------------------------------------------------------------
  758. // Purpose:
  759. //-----------------------------------------------------------------------------
  760. BASEPTR CBaseEntity::ThinkSet( BASEPTR func, float thinkTime, const char *szContext )
  761. {
  762. #if !defined( CLIENT_DLL )
  763. #if defined( _DEBUG )
  764. #if defined( __clang__ )
  765. COMPILE_TIME_ASSERT( sizeof( func ) == sizeof( m_pfnThink ) );
  766. #elif defined( GNUC ) || defined( COMPILER_PS3 ) || defined( PLATFORM_64BITS )
  767. COMPILE_TIME_ASSERT( sizeof(func) == 8 );
  768. #else
  769. COMPILE_TIME_ASSERT( sizeof(func) == 4 );
  770. #endif
  771. #endif
  772. #endif
  773. // Old system?
  774. if ( !szContext )
  775. {
  776. m_pfnThink = func;
  777. #if !defined( CLIENT_DLL )
  778. #ifdef _DEBUG
  779. FunctionCheck( reinterpret_cast<inputfunc_t>(m_pfnThink), "BaseThinkFunc" );
  780. #endif
  781. #endif
  782. return m_pfnThink;
  783. }
  784. // Find the think function in our list, and if we couldn't find it, register it
  785. int iIndex = GetIndexForThinkContext( szContext );
  786. if ( iIndex == NO_THINK_CONTEXT )
  787. {
  788. iIndex = RegisterThinkContext( szContext );
  789. }
  790. m_aThinkFunctions[ iIndex ].m_pfnThink = func;
  791. #if !defined( CLIENT_DLL )
  792. #ifdef _DEBUG
  793. FunctionCheck( reinterpret_cast<inputfunc_t>(m_aThinkFunctions[ iIndex ].m_pfnThink), szContext );
  794. #endif
  795. #endif
  796. if ( thinkTime != 0 )
  797. {
  798. int thinkTick = ( thinkTime == TICK_NEVER_THINK ) ? TICK_NEVER_THINK : TIME_TO_TICKS( thinkTime );
  799. m_aThinkFunctions[ iIndex ].m_nNextThinkTick = thinkTick;
  800. CheckHasThinkFunction( thinkTick == TICK_NEVER_THINK ? false : true );
  801. }
  802. return func;
  803. }
  804. //-----------------------------------------------------------------------------
  805. // Purpose:
  806. //-----------------------------------------------------------------------------
  807. void CBaseEntity::SetNextThink( float thinkTime, const char *szContext )
  808. {
  809. int thinkTick = ( thinkTime == TICK_NEVER_THINK ) ? TICK_NEVER_THINK : TIME_TO_TICKS( thinkTime );
  810. // Are we currently in a think function with a context?
  811. int iIndex = 0;
  812. if ( !szContext )
  813. {
  814. #ifdef _DEBUG
  815. if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
  816. {
  817. Msg( "Warning: Setting base think function within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
  818. }
  819. #endif
  820. // Old system
  821. m_nNextThinkTick = thinkTick;
  822. CheckHasThinkFunction( thinkTick == TICK_NEVER_THINK ? false : true );
  823. return;
  824. }
  825. else
  826. {
  827. // Find the think function in our list, and if we couldn't find it, register it
  828. iIndex = GetIndexForThinkContext( szContext );
  829. if ( iIndex == NO_THINK_CONTEXT )
  830. {
  831. iIndex = RegisterThinkContext( szContext );
  832. }
  833. }
  834. // Old system
  835. m_aThinkFunctions[ iIndex ].m_nNextThinkTick = thinkTick;
  836. CheckHasThinkFunction( thinkTick == TICK_NEVER_THINK ? false : true );
  837. }
  838. //-----------------------------------------------------------------------------
  839. // Purpose:
  840. //-----------------------------------------------------------------------------
  841. float CBaseEntity::GetNextThink( const char *szContext )
  842. {
  843. // Are we currently in a think function with a context?
  844. int iIndex = 0;
  845. if ( !szContext )
  846. {
  847. #ifdef _DEBUG
  848. if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
  849. {
  850. Msg( "Warning: Getting base nextthink time within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
  851. }
  852. #endif
  853. if ( m_nNextThinkTick == TICK_NEVER_THINK )
  854. return TICK_NEVER_THINK;
  855. // Old system
  856. return TICK_INTERVAL * (m_nNextThinkTick );
  857. }
  858. else
  859. {
  860. // Find the think function in our list
  861. iIndex = GetIndexForThinkContext( szContext );
  862. }
  863. if ( iIndex == m_aThinkFunctions.InvalidIndex() )
  864. return TICK_NEVER_THINK;
  865. if ( m_aThinkFunctions[ iIndex ].m_nNextThinkTick == TICK_NEVER_THINK )
  866. {
  867. return TICK_NEVER_THINK;
  868. }
  869. return TICK_INTERVAL * (m_aThinkFunctions[ iIndex ].m_nNextThinkTick );
  870. }
  871. int CBaseEntity::GetNextThinkTick( const char *szContext /*= NULL*/ )
  872. {
  873. // Are we currently in a think function with a context?
  874. int iIndex = 0;
  875. if ( !szContext )
  876. {
  877. #ifdef _DEBUG
  878. if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
  879. {
  880. Msg( "Warning: Getting base nextthink time within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
  881. }
  882. #endif
  883. if ( m_nNextThinkTick == TICK_NEVER_THINK )
  884. return TICK_NEVER_THINK;
  885. // Old system
  886. return m_nNextThinkTick;
  887. }
  888. else
  889. {
  890. // Find the think function in our list
  891. iIndex = GetIndexForThinkContext( szContext );
  892. // Looking up an invalid think context!
  893. Assert( iIndex != -1 );
  894. }
  895. if ( ( iIndex == -1 ) || ( m_aThinkFunctions[ iIndex ].m_nNextThinkTick == TICK_NEVER_THINK ) )
  896. {
  897. return TICK_NEVER_THINK;
  898. }
  899. return m_aThinkFunctions[ iIndex ].m_nNextThinkTick;
  900. }
  901. //-----------------------------------------------------------------------------
  902. // Purpose:
  903. //-----------------------------------------------------------------------------
  904. float CBaseEntity::GetLastThink( const char *szContext )
  905. {
  906. // Are we currently in a think function with a context?
  907. int iIndex = 0;
  908. if ( !szContext )
  909. {
  910. #ifdef _DEBUG
  911. if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
  912. {
  913. Msg( "Warning: Getting base lastthink time within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
  914. }
  915. #endif
  916. // Old system
  917. return m_nLastThinkTick * TICK_INTERVAL;
  918. }
  919. else
  920. {
  921. // Find the think function in our list
  922. iIndex = GetIndexForThinkContext( szContext );
  923. }
  924. return m_aThinkFunctions[ iIndex ].m_nLastThinkTick * TICK_INTERVAL;
  925. }
  926. int CBaseEntity::GetLastThinkTick( const char *szContext /*= NULL*/ )
  927. {
  928. // Are we currently in a think function with a context?
  929. int iIndex = 0;
  930. if ( !szContext )
  931. {
  932. #ifdef _DEBUG
  933. if ( m_iCurrentThinkContext != NO_THINK_CONTEXT )
  934. {
  935. Msg( "Warning: Getting base lastthink time within think context %s\n", STRING(m_aThinkFunctions[m_iCurrentThinkContext].m_iszContext) );
  936. }
  937. #endif
  938. // Old system
  939. return m_nLastThinkTick;
  940. }
  941. else
  942. {
  943. // Find the think function in our list
  944. iIndex = GetIndexForThinkContext( szContext );
  945. }
  946. return m_aThinkFunctions[ iIndex ].m_nLastThinkTick;
  947. }
  948. bool CBaseEntity::WillThink()
  949. {
  950. if ( m_nNextThinkTick > 0 )
  951. return true;
  952. for ( int i = 0; i < m_aThinkFunctions.Count(); i++ )
  953. {
  954. if ( m_aThinkFunctions[i].m_nNextThinkTick > 0 )
  955. return true;
  956. }
  957. return false;
  958. }
  959. #if !defined( CLIENT_DLL )
  960. // Rebase all the current ticks in the think functions as delta ticks or from delta ticks to absolute ticks
  961. void CBaseEntity::RebaseThinkTicks( bool bMakeDeltas )
  962. {
  963. int nCurTick = TIME_TO_TICKS( gpGlobals->curtime );
  964. for ( int i = 0; i < m_aThinkFunctions.Count(); i++ )
  965. {
  966. if ( m_aThinkFunctions[i].m_nNextThinkTick > 0 )
  967. {
  968. if ( bMakeDeltas )
  969. {
  970. // Turn into a delta value
  971. m_aThinkFunctions[i].m_nNextThinkTick = m_aThinkFunctions[i].m_nNextThinkTick - nCurTick;
  972. m_aThinkFunctions[i].m_nLastThinkTick = m_aThinkFunctions[i].m_nLastThinkTick - nCurTick;
  973. }
  974. else
  975. {
  976. // Change a delta to an absolute tick value
  977. m_aThinkFunctions[i].m_nNextThinkTick = m_aThinkFunctions[i].m_nNextThinkTick + nCurTick;
  978. m_aThinkFunctions[i].m_nLastThinkTick = m_aThinkFunctions[i].m_nLastThinkTick + nCurTick;
  979. }
  980. }
  981. }
  982. }
  983. #endif // !CLIENT_DLL
  984. // returns the first tick the entity will run any think function
  985. // returns TICK_NEVER_THINK if no think functions are scheduled
  986. int CBaseEntity::GetFirstThinkTick()
  987. {
  988. int minTick = TICK_NEVER_THINK;
  989. if ( m_nNextThinkTick > 0 )
  990. {
  991. minTick = m_nNextThinkTick;
  992. }
  993. for ( int i = 0; i < m_aThinkFunctions.Count(); i++ )
  994. {
  995. int next = m_aThinkFunctions[i].m_nNextThinkTick;
  996. if ( next > 0 )
  997. {
  998. if ( next < minTick || minTick == TICK_NEVER_THINK )
  999. {
  1000. minTick = next;
  1001. }
  1002. }
  1003. }
  1004. return minTick;
  1005. }
  1006. // NOTE: pass in the isThinking hint so we have to search the think functions less
  1007. void CBaseEntity::CheckHasThinkFunction( bool isThinking )
  1008. {
  1009. if ( IsEFlagSet( EFL_NO_THINK_FUNCTION ) && isThinking )
  1010. {
  1011. RemoveEFlags( EFL_NO_THINK_FUNCTION );
  1012. }
  1013. else if ( !isThinking && !IsEFlagSet( EFL_NO_THINK_FUNCTION ) && !WillThink() )
  1014. {
  1015. AddEFlags( EFL_NO_THINK_FUNCTION );
  1016. }
  1017. #if !defined( CLIENT_DLL )
  1018. SimThink_EntityChanged( this );
  1019. #endif
  1020. }
  1021. bool CBaseEntity::WillSimulateGamePhysics()
  1022. {
  1023. // players always simulate game physics
  1024. if ( !IsPlayer() )
  1025. {
  1026. MoveType_t movetype = GetMoveType();
  1027. if ( movetype == MOVETYPE_NONE || movetype == MOVETYPE_VPHYSICS )
  1028. return false;
  1029. #if !defined( CLIENT_DLL )
  1030. // MOVETYPE_PUSH not supported on the client
  1031. if ( movetype == MOVETYPE_PUSH && GetMoveDoneTime() <= 0 )
  1032. return false;
  1033. #endif
  1034. }
  1035. return true;
  1036. }
  1037. void CBaseEntity::CheckHasGamePhysicsSimulation()
  1038. {
  1039. bool isSimulating = WillSimulateGamePhysics();
  1040. if ( isSimulating != IsEFlagSet(EFL_NO_GAME_PHYSICS_SIMULATION) )
  1041. return;
  1042. if ( isSimulating )
  1043. {
  1044. RemoveEFlags( EFL_NO_GAME_PHYSICS_SIMULATION );
  1045. }
  1046. else
  1047. {
  1048. AddEFlags( EFL_NO_GAME_PHYSICS_SIMULATION );
  1049. }
  1050. #if !defined( CLIENT_DLL )
  1051. SimThink_EntityChanged( this );
  1052. #endif
  1053. }
  1054. //-----------------------------------------------------------------------------
  1055. // Sets/Gets the next think based on context index
  1056. //-----------------------------------------------------------------------------
  1057. void CBaseEntity::SetNextThink( int nContextIndex, float thinkTime )
  1058. {
  1059. int thinkTick = ( thinkTime == TICK_NEVER_THINK ) ? TICK_NEVER_THINK : TIME_TO_TICKS( thinkTime );
  1060. if (nContextIndex < 0)
  1061. {
  1062. SetNextThink( thinkTime );
  1063. }
  1064. else
  1065. {
  1066. m_aThinkFunctions[nContextIndex].m_nNextThinkTick = thinkTick;
  1067. }
  1068. CheckHasThinkFunction( thinkTick == TICK_NEVER_THINK ? false : true );
  1069. }
  1070. void CBaseEntity::SetLastThink( int nContextIndex, float thinkTime )
  1071. {
  1072. int thinkTick = ( thinkTime == TICK_NEVER_THINK ) ? TICK_NEVER_THINK : TIME_TO_TICKS( thinkTime );
  1073. if (nContextIndex < 0)
  1074. {
  1075. m_nLastThinkTick = thinkTick;
  1076. }
  1077. else
  1078. {
  1079. m_aThinkFunctions[nContextIndex].m_nLastThinkTick = thinkTick;
  1080. }
  1081. }
  1082. float CBaseEntity::GetNextThink( int nContextIndex ) const
  1083. {
  1084. if (nContextIndex < 0)
  1085. return m_nNextThinkTick * TICK_INTERVAL;
  1086. return m_aThinkFunctions[nContextIndex].m_nNextThinkTick * TICK_INTERVAL;
  1087. }
  1088. int CBaseEntity::GetNextThinkTick( int nContextIndex ) const
  1089. {
  1090. if (nContextIndex < 0)
  1091. return m_nNextThinkTick;
  1092. return m_aThinkFunctions[nContextIndex].m_nNextThinkTick;
  1093. }
  1094. int CheckEntityVelocity( Vector &v )
  1095. {
  1096. // If we're not clamping, then return that everything is fine, just fine.
  1097. if ( !sv_clamp_unsafe_velocities.GetBool() )
  1098. return 1;
  1099. float r = k_flMaxEntitySpeed;
  1100. if (
  1101. v.x > -r && v.x < r &&
  1102. v.y > -r && v.y < r &&
  1103. v.z > -r && v.z < r )
  1104. {
  1105. // The usual case. It's totally reasonable
  1106. return 1;
  1107. }
  1108. float speed = v.Length();
  1109. if ( speed < k_flMaxEntitySpeed * 100.0f )
  1110. {
  1111. // Sort of suspicious. Clamp it
  1112. v *= k_flMaxEntitySpeed / speed;
  1113. return 0;
  1114. }
  1115. // A terrible, horrible, no good, very bad velocity.
  1116. return -1;
  1117. }
  1118. //-----------------------------------------------------------------------------
  1119. // Purpose: My physics object has been updated, react or extract data
  1120. //-----------------------------------------------------------------------------
  1121. void CBaseEntity::VPhysicsUpdate( IPhysicsObject *pPhysics )
  1122. {
  1123. switch( GetMoveType() )
  1124. {
  1125. case MOVETYPE_VPHYSICS:
  1126. {
  1127. if ( GetMoveParent() )
  1128. {
  1129. return;
  1130. }
  1131. Vector origin;
  1132. QAngle angles;
  1133. pPhysics->GetPosition( &origin, &angles );
  1134. if ( !IsEntityQAngleReasonable( angles ) )
  1135. {
  1136. if ( CheckEmitReasonablePhysicsSpew() )
  1137. {
  1138. Warning( "Ignoring bogus angles (%f,%f,%f) from vphysics! (entity %s)\n", angles.x, angles.y, angles.z, GetDebugName() );
  1139. }
  1140. angles = vec3_angle;
  1141. }
  1142. #ifndef CLIENT_DLL
  1143. Vector prevOrigin = GetAbsOrigin();
  1144. #endif
  1145. if ( IsEntityPositionReasonable( origin ) )
  1146. {
  1147. SetAbsOrigin( origin );
  1148. }
  1149. else
  1150. {
  1151. if ( CheckEmitReasonablePhysicsSpew() )
  1152. {
  1153. Warning( "Ignoring unreasonable position (%f,%f,%f) from vphysics! (entity %s)\n", origin.x, origin.y, origin.z, GetDebugName() );
  1154. }
  1155. }
  1156. for ( int i = 0; i < 3; ++i )
  1157. {
  1158. angles[ i ] = AngleNormalize( angles[ i ] );
  1159. }
  1160. #ifndef CLIENT_DLL
  1161. NetworkQuantize( origin, angles );
  1162. #endif
  1163. if ( origin.IsValid() )
  1164. {
  1165. SetAbsOrigin( origin );
  1166. }
  1167. else
  1168. {
  1169. Msg( "Infinite origin from vphysics! (entity %s)\n", GetDebugName() );
  1170. }
  1171. SetAbsAngles( angles );
  1172. // Interactive debris converts back to debris when it comes to rest
  1173. if ( pPhysics->IsAsleep() && GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
  1174. {
  1175. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  1176. }
  1177. #ifndef CLIENT_DLL
  1178. PhysicsTouchTriggers( &prevOrigin );
  1179. PhysicsRelinkChildren(gpGlobals->frametime);
  1180. #endif
  1181. }
  1182. break;
  1183. case MOVETYPE_STEP:
  1184. break;
  1185. case MOVETYPE_PUSH:
  1186. #ifndef CLIENT_DLL
  1187. VPhysicsUpdatePusher( pPhysics );
  1188. #endif
  1189. break;
  1190. }
  1191. }
  1192. //-----------------------------------------------------------------------------
  1193. // Purpose: Init this object's physics as a static
  1194. //-----------------------------------------------------------------------------
  1195. IPhysicsObject *CBaseEntity::VPhysicsInitStatic( void )
  1196. {
  1197. if ( !VPhysicsInitSetup() )
  1198. return NULL;
  1199. #ifndef CLIENT_DLL
  1200. // If this entity has a move parent, it needs to be shadow, not static
  1201. if ( GetMoveParent() )
  1202. {
  1203. // must be SOLID_VPHYSICS if in hierarchy to solve collisions correctly
  1204. if ( GetSolid() == SOLID_BSP && GetRootMoveParent()->GetSolid() != SOLID_BSP )
  1205. {
  1206. SetSolid( SOLID_VPHYSICS );
  1207. }
  1208. return VPhysicsInitShadow( false, false );
  1209. }
  1210. #endif
  1211. // No physics
  1212. if ( GetSolid() == SOLID_NONE )
  1213. return NULL;
  1214. // create a static physics objct
  1215. IPhysicsObject *pPhysicsObject = NULL;
  1216. if ( GetSolid() == SOLID_BBOX )
  1217. {
  1218. pPhysicsObject = PhysModelCreateBox( this, WorldAlignMins(), WorldAlignMaxs(), GetAbsOrigin(), true );
  1219. }
  1220. else if ( GetSolid() == SOLID_OBB )
  1221. {
  1222. pPhysicsObject = PhysModelCreateOBB( this, CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), GetAbsOrigin(), GetAbsAngles(), true );
  1223. }
  1224. else
  1225. {
  1226. pPhysicsObject = PhysModelCreateUnmoveable( this, GetModelIndex(), GetAbsOrigin(), GetAbsAngles() );
  1227. }
  1228. VPhysicsSetObject( pPhysicsObject );
  1229. return pPhysicsObject;
  1230. }
  1231. //-----------------------------------------------------------------------------
  1232. // Purpose:
  1233. // Input : *pPhysics -
  1234. //-----------------------------------------------------------------------------
  1235. void CBaseEntity::VPhysicsSetObject( IPhysicsObject *pPhysics )
  1236. {
  1237. if ( m_pPhysicsObject && pPhysics )
  1238. {
  1239. Warning( "Overwriting physics object for %s\n", GetClassname() );
  1240. }
  1241. m_pPhysicsObject = pPhysics;
  1242. #ifndef CLIENT_DLL
  1243. RemoveSolidFlags(FSOLID_NOT_MOVEABLE);
  1244. #endif
  1245. if ( m_pPhysicsObject )
  1246. {
  1247. m_flNonShadowMass = m_pPhysicsObject->GetMass();
  1248. #ifndef CLIENT_DLL
  1249. if ( m_pPhysicsObject->IsStatic() )
  1250. {
  1251. AddSolidFlags(FSOLID_NOT_MOVEABLE);
  1252. }
  1253. #endif
  1254. }
  1255. if ( pPhysics && !m_pPhysicsObject )
  1256. {
  1257. CollisionRulesChanged();
  1258. }
  1259. }
  1260. void CBaseEntity::VPhysicsSwapObject( IPhysicsObject *pSwap )
  1261. {
  1262. if ( !pSwap )
  1263. {
  1264. PhysRemoveShadow(this);
  1265. }
  1266. if ( !m_pPhysicsObject )
  1267. {
  1268. Warning( "Bad vphysics swap for %s\n", STRING(m_iClassname) );
  1269. }
  1270. m_pPhysicsObject = pSwap;
  1271. }
  1272. //-----------------------------------------------------------------------------
  1273. // Purpose:
  1274. //-----------------------------------------------------------------------------
  1275. void CBaseEntity::VPhysicsDestroyObject( void )
  1276. {
  1277. if ( m_pPhysicsObject )
  1278. {
  1279. #ifndef CLIENT_DLL
  1280. PhysRemoveShadow( this );
  1281. #endif
  1282. PhysDestroyObject( m_pPhysicsObject, this );
  1283. m_pPhysicsObject = NULL;
  1284. }
  1285. }
  1286. //-----------------------------------------------------------------------------
  1287. // Purpose:
  1288. //-----------------------------------------------------------------------------
  1289. bool CBaseEntity::VPhysicsInitSetup()
  1290. {
  1291. #ifndef CLIENT_DLL
  1292. // don't support logical ents
  1293. if ( !edict() || IsMarkedForDeletion() )
  1294. return false;
  1295. #endif
  1296. // If this entity already has a physics object, then it should have been deleted prior to making this call.
  1297. Assert(!m_pPhysicsObject);
  1298. VPhysicsDestroyObject();
  1299. m_flNonShadowMass = -1.0f;
  1300. // make sure absorigin / absangles are correct
  1301. return true;
  1302. }
  1303. //-----------------------------------------------------------------------------
  1304. // Purpose: This creates a normal vphysics simulated object
  1305. // physics alone determines where it goes (gravity, friction, etc)
  1306. // and the entity receives updates from vphysics. SetAbsOrigin(), etc do not affect the object!
  1307. //-----------------------------------------------------------------------------
  1308. IPhysicsObject *CBaseEntity::VPhysicsInitNormal( SolidType_t solidType, int nSolidFlags, bool createAsleep, solid_t *pSolid )
  1309. {
  1310. if ( !VPhysicsInitSetup() )
  1311. return NULL;
  1312. // NOTE: This has to occur before PhysModelCreate because that call will
  1313. // call back into ShouldCollide(), which uses solidtype for rules.
  1314. SetSolid( solidType );
  1315. SetSolidFlags( nSolidFlags );
  1316. // No physics
  1317. if ( solidType == SOLID_NONE )
  1318. {
  1319. return NULL;
  1320. }
  1321. // create a normal physics object
  1322. IPhysicsObject *pPhysicsObject = PhysModelCreate( this, GetModelIndex(), GetAbsOrigin(), GetAbsAngles(), pSolid );
  1323. if ( pPhysicsObject )
  1324. {
  1325. VPhysicsSetObject( pPhysicsObject );
  1326. SetMoveType( MOVETYPE_VPHYSICS );
  1327. if ( !createAsleep )
  1328. {
  1329. pPhysicsObject->Wake();
  1330. }
  1331. }
  1332. return pPhysicsObject;
  1333. }
  1334. // This creates a vphysics object with a shadow controller that follows the AI
  1335. IPhysicsObject *CBaseEntity::VPhysicsInitShadow( bool allowPhysicsMovement, bool allowPhysicsRotation, solid_t *pSolid )
  1336. {
  1337. if ( !VPhysicsInitSetup() )
  1338. return NULL;
  1339. // No physics
  1340. if ( GetSolid() == SOLID_NONE )
  1341. return NULL;
  1342. const Vector &origin = GetAbsOrigin();
  1343. QAngle angles = GetAbsAngles();
  1344. IPhysicsObject *pPhysicsObject = NULL;
  1345. if ( GetSolid() == SOLID_BBOX )
  1346. {
  1347. // adjust these so the game tracing epsilons match the physics minimum separation distance
  1348. // this will shrink the vphysics version of the model by the difference in epsilons
  1349. float radius = 0.25f - DIST_EPSILON;
  1350. Vector mins = WorldAlignMins() + Vector(radius, radius, radius);
  1351. Vector maxs = WorldAlignMaxs() - Vector(radius, radius, radius);
  1352. pPhysicsObject = PhysModelCreateBox( this, mins, maxs, origin, false );
  1353. angles = vec3_angle;
  1354. }
  1355. else if ( GetSolid() == SOLID_OBB )
  1356. {
  1357. pPhysicsObject = PhysModelCreateOBB( this, CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), origin, angles, false );
  1358. }
  1359. else
  1360. {
  1361. pPhysicsObject = PhysModelCreate( this, GetModelIndex(), origin, angles, pSolid );
  1362. }
  1363. if ( !pPhysicsObject )
  1364. return NULL;
  1365. VPhysicsSetObject( pPhysicsObject );
  1366. // UNDONE: Tune these speeds!!!
  1367. pPhysicsObject->SetShadow( 1e4, 1e4, allowPhysicsMovement, allowPhysicsRotation );
  1368. pPhysicsObject->UpdateShadow( origin, angles, false, 0 );
  1369. return pPhysicsObject;
  1370. }
  1371. //-----------------------------------------------------------------------------
  1372. // Purpose:
  1373. //-----------------------------------------------------------------------------
  1374. bool CBaseEntity::CreateVPhysics()
  1375. {
  1376. return false;
  1377. }
  1378. bool CBaseEntity::IsStandable() const
  1379. {
  1380. if (GetSolidFlags() & FSOLID_NOT_STANDABLE)
  1381. return false;
  1382. if ( GetSolid() == SOLID_BSP || GetSolid() == SOLID_VPHYSICS || GetSolid() == SOLID_BBOX )
  1383. return true;
  1384. return IsBSPModel( );
  1385. }
  1386. bool CBaseEntity::IsBSPModel() const
  1387. {
  1388. if ( GetSolid() == SOLID_BSP )
  1389. return true;
  1390. const model_t *model = modelinfo->GetModel( GetModelIndex() );
  1391. if ( GetSolid() == SOLID_VPHYSICS && modelinfo->GetModelType( model ) == mod_brush )
  1392. return true;
  1393. return false;
  1394. }
  1395. //-----------------------------------------------------------------------------
  1396. // Invalidates the abs state of all children
  1397. //-----------------------------------------------------------------------------
  1398. void CBaseEntity::InvalidatePhysicsRecursive( int nChangeFlags )
  1399. {
  1400. // Main entry point for dirty flag setting for the 90% case
  1401. // 1) If the origin changes, then we have to update abstransform, Shadow projection, PVS, KD-tree,
  1402. // client-leaf system.
  1403. // 2) If the angles change, then we have to update abstransform, Shadow projection,
  1404. // shadow render-to-texture, client-leaf system, and surrounding bounds.
  1405. // Children have to additionally update absvelocity, KD-tree, and PVS.
  1406. // If the surrounding bounds actually update, when we also need to update the KD-tree and the PVS.
  1407. // 3) If it's due to attachment, then all children who are attached to an attachment point
  1408. // are assumed to have dirty origin + angles.
  1409. // Other stuff:
  1410. // 1) Marking the surrounding bounds dirty will automatically mark KD tree + PVS dirty.
  1411. int nDirtyFlags = 0;
  1412. if ( nChangeFlags & VELOCITY_CHANGED )
  1413. {
  1414. nDirtyFlags |= EFL_DIRTY_ABSVELOCITY;
  1415. }
  1416. bool bSurroundDirty = false;
  1417. if ( nChangeFlags & POSITION_CHANGED )
  1418. {
  1419. nDirtyFlags |= EFL_DIRTY_ABSTRANSFORM;
  1420. #ifndef CLIENT_DLL
  1421. NetworkProp()->MarkPVSInformationDirty();
  1422. #endif
  1423. // NOTE: This will also mark shadow projection + client leaf dirty
  1424. if ( entindex() != 0 )
  1425. {
  1426. CollisionProp()->MarkPartitionHandleDirty();
  1427. }
  1428. }
  1429. // NOTE: This has to be done after velocity + position are changed
  1430. // because we change the nChangeFlags for the child entities
  1431. if ( nChangeFlags & ANGLES_CHANGED )
  1432. {
  1433. nDirtyFlags |= EFL_DIRTY_ABSTRANSFORM;
  1434. if ( CollisionProp()->DoesRotationInvalidateSurroundingBox() )
  1435. {
  1436. // NOTE: This will handle the KD-tree, surrounding bounds, PVS
  1437. // render-to-texture shadow, shadow projection, and client leaf dirty
  1438. CollisionProp()->MarkSurroundingBoundsDirty();
  1439. bSurroundDirty = true;
  1440. }
  1441. // This is going to be used for all children: children
  1442. // have position + velocity changed
  1443. nChangeFlags |= POSITION_CHANGED | VELOCITY_CHANGED;
  1444. }
  1445. if ( nChangeFlags & SEQUENCE_CHANGED )
  1446. {
  1447. if ( !bSurroundDirty )
  1448. {
  1449. if ( CollisionProp()->DoesSequenceChangeInvalidateSurroundingBox() )
  1450. {
  1451. // NOTE: This will handle the KD-tree, surrounding bounds, PVS
  1452. // render-to-texture shadow, shadow projection, and client leaf dirty
  1453. CollisionProp()->MarkSurroundingBoundsDirty();
  1454. bSurroundDirty = true;
  1455. }
  1456. }
  1457. // Children sequences do not change as a result of parent sequence changes
  1458. nChangeFlags &= ~SEQUENCE_CHANGED;
  1459. }
  1460. #ifdef CLIENT_DLL
  1461. if ( !bSurroundDirty && (nChangeFlags & (POSITION_CHANGED|ANGLES_CHANGED|BOUNDS_CHANGED)) )
  1462. {
  1463. if ( entindex() != 0 )
  1464. {
  1465. MarkRenderHandleDirty();
  1466. g_pClientShadowMgr->AddToDirtyShadowList( this );
  1467. g_pClientShadowMgr->MarkRenderToTextureShadowDirty( GetShadowHandle() );
  1468. }
  1469. }
  1470. #endif
  1471. AddEFlags( nDirtyFlags );
  1472. // Set flags for children
  1473. bool bOnlyDueToAttachment = false;
  1474. if ( nChangeFlags & ( ANIMATION_CHANGED | BOUNDS_CHANGED ) )
  1475. {
  1476. #ifdef CLIENT_DLL
  1477. if ( ( nChangeFlags & BOUNDS_CHANGED ) == 0 )
  1478. {
  1479. g_pClientShadowMgr->MarkRenderToTextureShadowDirty( GetShadowHandle() );
  1480. }
  1481. #endif
  1482. // Only set this flag if the only thing that changed us was the animation.
  1483. // If position or something else changed us, then we must tell all children.
  1484. if ( !( nChangeFlags & (POSITION_CHANGED | VELOCITY_CHANGED | ANGLES_CHANGED) ) )
  1485. {
  1486. bOnlyDueToAttachment = true;
  1487. }
  1488. nChangeFlags = POSITION_CHANGED | ANGLES_CHANGED | VELOCITY_CHANGED;
  1489. }
  1490. for (CBaseEntity *pChild = FirstMoveChild(); pChild; pChild = pChild->NextMovePeer())
  1491. {
  1492. // If this is due to the parent animating, only invalidate children that are parented to an attachment
  1493. // Entities that are following also access attachments points on parents and must be invalidated.
  1494. if ( bOnlyDueToAttachment )
  1495. {
  1496. #ifdef CLIENT_DLL
  1497. if ( (pChild->GetParentAttachment() == 0) && !pChild->IsFollowingEntity() )
  1498. continue;
  1499. #else
  1500. if ( pChild->GetParentAttachment() == 0 )
  1501. continue;
  1502. #endif
  1503. }
  1504. pChild->InvalidatePhysicsRecursive( nChangeFlags );
  1505. }
  1506. // Clear out cached bones
  1507. if ( nChangeFlags & (POSITION_CHANGED | ANGLES_CHANGED | ANIMATION_CHANGED) )
  1508. {
  1509. CBaseAnimating *pAnim = GetBaseAnimating();
  1510. if ( pAnim )
  1511. pAnim->InvalidateBoneCache();
  1512. }
  1513. }
  1514. //-----------------------------------------------------------------------------
  1515. // Returns the highest parent of an entity
  1516. //-----------------------------------------------------------------------------
  1517. CBaseEntity *CBaseEntity::GetRootMoveParent()
  1518. {
  1519. CBaseEntity *pEntity = this;
  1520. CBaseEntity *pParent = this->GetMoveParent();
  1521. while ( pParent )
  1522. {
  1523. pEntity = pParent;
  1524. pParent = pEntity->GetMoveParent();
  1525. }
  1526. return pEntity;
  1527. }
  1528. //-----------------------------------------------------------------------------
  1529. // Purpose: static method
  1530. // Output : Returns true on success, false on failure.
  1531. //-----------------------------------------------------------------------------
  1532. bool CBaseEntity::IsPrecacheAllowed()
  1533. {
  1534. return m_bAllowPrecache;
  1535. }
  1536. //-----------------------------------------------------------------------------
  1537. // Purpose: static method
  1538. // Input : allow -
  1539. //-----------------------------------------------------------------------------
  1540. void CBaseEntity::SetAllowPrecache( bool allow )
  1541. {
  1542. m_bAllowPrecache = allow;
  1543. }
  1544. /*
  1545. ================
  1546. FireBullets
  1547. Go to the trouble of combining multiple pellets into a single damage call.
  1548. ================
  1549. */
  1550. #if defined( GAME_DLL )
  1551. class CBulletsTraceFilter : public CTraceFilterSimpleList
  1552. {
  1553. public:
  1554. CBulletsTraceFilter( int collisionGroup ) : CTraceFilterSimpleList( collisionGroup ) {}
  1555. bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  1556. {
  1557. if ( m_PassEntities.Count() )
  1558. {
  1559. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  1560. CBaseEntity *pPassEntity = EntityFromEntityHandle( m_PassEntities[0] );
  1561. if ( pEntity && pPassEntity && pEntity->GetOwnerEntity() == pPassEntity &&
  1562. pPassEntity->IsSolidFlagSet(FSOLID_NOT_SOLID) && pPassEntity->IsSolidFlagSet( FSOLID_CUSTOMBOXTEST ) &&
  1563. pPassEntity->IsSolidFlagSet( FSOLID_CUSTOMRAYTEST ) )
  1564. {
  1565. // It's a bone follower of the entity to ignore (toml 8/3/2007)
  1566. return false;
  1567. }
  1568. }
  1569. return CTraceFilterSimpleList::ShouldHitEntity( pHandleEntity, contentsMask );
  1570. }
  1571. };
  1572. #else
  1573. typedef CTraceFilterSimpleList CBulletsTraceFilter;
  1574. #endif
  1575. void CBaseEntity::FireBullets( const FireBulletsInfo_t &info )
  1576. {
  1577. static int tracerCount;
  1578. trace_t tr;
  1579. CAmmoDef* pAmmoDef = GetAmmoDef();
  1580. int nDamageType = pAmmoDef->DamageType(info.m_iAmmoType);
  1581. int nAmmoFlags = pAmmoDef->Flags(info.m_iAmmoType);
  1582. bool bDoServerEffects = true;
  1583. #if defined( GAME_DLL )
  1584. if( IsPlayer() )
  1585. {
  1586. CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>(this);
  1587. int rumbleEffect = pPlayer->GetActiveWeapon()->GetRumbleEffect();
  1588. if( rumbleEffect != RUMBLE_INVALID )
  1589. {
  1590. if( rumbleEffect == RUMBLE_SHOTGUN_SINGLE )
  1591. {
  1592. if( info.m_iShots == 12 )
  1593. {
  1594. // Upgrade to double barrel rumble effect
  1595. rumbleEffect = RUMBLE_SHOTGUN_DOUBLE;
  1596. }
  1597. }
  1598. pPlayer->RumbleEffect( rumbleEffect, 0, RUMBLE_FLAG_RESTART );
  1599. }
  1600. }
  1601. #endif// GAME_DLL
  1602. float flPlayerDamage = info.m_flPlayerDamage;
  1603. if ( flPlayerDamage == 0.0f )
  1604. {
  1605. if ( nAmmoFlags & AMMO_INTERPRET_PLRDAMAGE_AS_DAMAGE_TO_PLAYER )
  1606. {
  1607. flPlayerDamage = pAmmoDef->PlrDamage( info.m_iAmmoType );
  1608. }
  1609. }
  1610. // the default attacker is ourselves
  1611. CBaseEntity *pAttacker = info.m_pAttacker ? info.m_pAttacker : this;
  1612. // Make sure we don't have a dangling damage target from a recursive call
  1613. if ( g_MultiDamage.GetTarget() != NULL )
  1614. {
  1615. ApplyMultiDamage();
  1616. }
  1617. ClearMultiDamage();
  1618. g_MultiDamage.SetDamageType( nDamageType | DMG_NEVERGIB );
  1619. Vector vecDir;
  1620. Vector vecEnd;
  1621. // Skip multiple entities when tracing
  1622. CBulletsTraceFilter traceFilter( COLLISION_GROUP_NONE );
  1623. traceFilter.SetPassEntity( this ); // Standard pass entity for THIS so that it can be easily removed from the list after passing through a portal
  1624. traceFilter.AddEntityToIgnore( info.m_pAdditionalIgnoreEnt );
  1625. #if defined( HL2_EPISODIC ) && defined( GAME_DLL )
  1626. // FIXME: We need to emulate this same behavior on the client as well -- jdw
  1627. // Also ignore a vehicle we're a passenger in
  1628. if ( MyCombatCharacterPointer() != NULL && MyCombatCharacterPointer()->IsInAVehicle() )
  1629. {
  1630. traceFilter.AddEntityToIgnore( MyCombatCharacterPointer()->GetVehicleEntity() );
  1631. }
  1632. #endif // SERVER_DLL
  1633. bool bUnderwaterBullets = ShouldDrawUnderwaterBulletBubbles();
  1634. bool bStartedInWater = false;
  1635. if ( bUnderwaterBullets )
  1636. {
  1637. bStartedInWater = ( enginetrace->GetPointContents( info.m_vecSrc, MASK_WATER ) & (CONTENTS_WATER|CONTENTS_SLIME) ) != 0;
  1638. }
  1639. // Prediction is only usable on players
  1640. int iSeed = 0;
  1641. if ( IsPlayer() )
  1642. {
  1643. iSeed = CBaseEntity::GetPredictionRandomSeed( SERVER_PLATTIME_RNG ) & 255;
  1644. }
  1645. //-----------------------------------------------------
  1646. // Set up our shot manipulator.
  1647. //-----------------------------------------------------
  1648. CShotManipulator Manipulator( info.m_vecDirShooting );
  1649. bool bDoImpacts = false;
  1650. bool bDoTracers = false;
  1651. float flCumulativeDamage = 0.0f;
  1652. for (int iShot = 0; iShot < info.m_iShots; iShot++)
  1653. {
  1654. bool bHitWater = false;
  1655. bool bHitGlass = false;
  1656. // Prediction is only usable on players
  1657. if ( IsPlayer() )
  1658. {
  1659. RandomSeed( iSeed ); // init random system with this seed
  1660. }
  1661. // If we're firing multiple shots, and the first shot has to be bang on target, ignore spread
  1662. if ( iShot == 0 && info.m_iShots > 1 && (info.m_nFlags & FIRE_BULLETS_FIRST_SHOT_ACCURATE) )
  1663. {
  1664. vecDir = Manipulator.GetShotDirection();
  1665. }
  1666. else
  1667. {
  1668. // Don't run the biasing code for the player at the moment.
  1669. vecDir = Manipulator.ApplySpread( info.m_vecSpread );
  1670. }
  1671. vecEnd = info.m_vecSrc + vecDir * info.m_flDistance;
  1672. #ifdef PORTAL
  1673. CPortal_Base2D *pShootThroughPortal = NULL;
  1674. float fPortalFraction = 2.0f;
  1675. #endif
  1676. if( IsPlayer() && info.m_iShots > 1 && iShot % 2 )
  1677. {
  1678. // Half of the shotgun pellets are hulls that make it easier to hit targets with the shotgun.
  1679. #ifdef PORTAL
  1680. Ray_t rayBullet;
  1681. rayBullet.Init( info.m_vecSrc, vecEnd );
  1682. pShootThroughPortal = UTIL_Portal_FirstAlongRay( rayBullet, fPortalFraction );
  1683. if ( !UTIL_Portal_TraceRay_Bullets( pShootThroughPortal, rayBullet, MASK_SHOT, &traceFilter, &tr ) )
  1684. {
  1685. pShootThroughPortal = NULL;
  1686. }
  1687. #else
  1688. AI_TraceHull( info.m_vecSrc, vecEnd, Vector( -3, -3, -3 ), Vector( 3, 3, 3 ), MASK_SHOT, &traceFilter, &tr );
  1689. #endif //#ifdef PORTAL
  1690. }
  1691. else
  1692. {
  1693. #ifdef PORTAL
  1694. Ray_t rayBullet;
  1695. rayBullet.Init( info.m_vecSrc, vecEnd );
  1696. pShootThroughPortal = UTIL_Portal_FirstAlongRay( rayBullet, fPortalFraction );
  1697. if ( !UTIL_Portal_TraceRay_Bullets( pShootThroughPortal, rayBullet, MASK_SHOT, &traceFilter, &tr ) )
  1698. {
  1699. pShootThroughPortal = NULL;
  1700. }
  1701. #else
  1702. AI_TraceLine(info.m_vecSrc, vecEnd, MASK_SHOT, &traceFilter, &tr);
  1703. #endif //#ifdef PORTAL
  1704. }
  1705. // Tracker 70354/63250: ywb 8/2/07
  1706. // Fixes bug where trace from turret with attachment point outside of Vcollide
  1707. // starts solid so doesn't hit anything else in the world and the final coord
  1708. // is outside of the MAX_COORD_FLOAT range. This cause trying to send the end pos
  1709. // of the tracer down to the client with an origin which is out-of-range for networking
  1710. if ( tr.startsolid )
  1711. {
  1712. tr.endpos = tr.startpos;
  1713. tr.fraction = 0.0f;
  1714. }
  1715. // bullet's final direction can be changed by passing through a portal
  1716. #ifdef PORTAL
  1717. if ( !tr.startsolid && tr.fraction > 0.0f )
  1718. {
  1719. vecDir = tr.endpos - tr.startpos;
  1720. VectorNormalize( vecDir );
  1721. }
  1722. #endif
  1723. #ifdef GAME_DLL
  1724. if ( ai_debug_shoot_positions.GetBool() )
  1725. NDebugOverlay::Line(info.m_vecSrc, vecEnd, 255, 255, 255, false, .1 );
  1726. #endif
  1727. if ( bStartedInWater )
  1728. {
  1729. #ifdef GAME_DLL
  1730. Vector vBubbleStart = info.m_vecSrc;
  1731. Vector vBubbleEnd = tr.endpos;
  1732. #ifdef PORTAL
  1733. if ( pShootThroughPortal )
  1734. {
  1735. vBubbleEnd = info.m_vecSrc + ( vecEnd - info.m_vecSrc ) * fPortalFraction;
  1736. }
  1737. #endif //#ifdef PORTAL
  1738. CreateBubbleTrailTracer( vBubbleStart, vBubbleEnd, vecDir );
  1739. #ifdef PORTAL
  1740. if ( pShootThroughPortal )
  1741. {
  1742. Vector vTransformedIntersection;
  1743. UTIL_Portal_PointTransform( pShootThroughPortal->MatrixThisToLinked(), vBubbleEnd, vTransformedIntersection );
  1744. CreateBubbleTrailTracer( vTransformedIntersection, tr.endpos, vecDir );
  1745. }
  1746. #endif //#ifdef PORTAL
  1747. #endif //#ifdef GAME_DLL
  1748. bHitWater = true;
  1749. }
  1750. // Now hit all triggers along the ray that respond to shots...
  1751. // Clip the ray to the first collided solid returned from traceline
  1752. CTakeDamageInfo triggerInfo( pAttacker, pAttacker, info.m_flDamage, nDamageType );
  1753. CalculateBulletDamageForce( &triggerInfo, info.m_iAmmoType, vecDir, tr.endpos );
  1754. triggerInfo.ScaleDamageForce( info.m_flDamageForceScale );
  1755. triggerInfo.SetAmmoType( info.m_iAmmoType );
  1756. #ifdef GAME_DLL
  1757. TraceAttackToTriggers( triggerInfo, tr.startpos, tr.endpos, vecDir );
  1758. #endif
  1759. // Make sure given a valid bullet type
  1760. if (info.m_iAmmoType == -1)
  1761. {
  1762. DevMsg("ERROR: Undefined ammo type!\n");
  1763. return;
  1764. }
  1765. Vector vecTracerDest = tr.endpos;
  1766. // do damage, paint decals
  1767. if (tr.fraction != 1.0)
  1768. {
  1769. #ifdef GAME_DLL
  1770. UpdateShotStatistics( tr );
  1771. // For shots that don't need persistance
  1772. int soundEntChannel = ( info.m_nFlags&FIRE_BULLETS_TEMPORARY_DANGER_SOUND ) ? SOUNDENT_CHANNEL_BULLET_IMPACT : SOUNDENT_CHANNEL_UNSPECIFIED;
  1773. CSoundEnt::InsertSound( SOUND_BULLET_IMPACT, tr.endpos, 200, 0.5, this, soundEntChannel );
  1774. #endif
  1775. // See if the bullet ended up underwater + started out of the water
  1776. if ( !bHitWater && ( enginetrace->GetPointContents( tr.endpos, MASK_WATER ) & (CONTENTS_WATER|CONTENTS_SLIME) ) )
  1777. {
  1778. bHitWater = HandleShotImpactingWater( info, vecEnd, &traceFilter, &vecTracerDest );
  1779. }
  1780. float flActualDamage = info.m_flDamage;
  1781. // If we hit a player, and we have player damage specified, use that instead
  1782. // Adrian: Make sure to use the currect value if we hit a vehicle the player is currently driving.
  1783. if ( flPlayerDamage != 0.0f )
  1784. {
  1785. if ( tr.m_pEnt->IsPlayer() )
  1786. {
  1787. flActualDamage = flPlayerDamage;
  1788. }
  1789. #ifdef GAME_DLL
  1790. else if ( tr.m_pEnt->GetServerVehicle() )
  1791. {
  1792. if ( tr.m_pEnt->GetServerVehicle()->GetPassenger() && tr.m_pEnt->GetServerVehicle()->GetPassenger()->IsPlayer() )
  1793. {
  1794. flActualDamage = flPlayerDamage;
  1795. }
  1796. }
  1797. #endif
  1798. }
  1799. int nActualDamageType = nDamageType;
  1800. if ( flActualDamage == 0.0 )
  1801. {
  1802. flActualDamage = g_pGameRules->GetAmmoDamage( pAttacker, tr.m_pEnt, info.m_iAmmoType );
  1803. }
  1804. else
  1805. {
  1806. nActualDamageType = nDamageType | ((flActualDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB );
  1807. }
  1808. if ( !bHitWater || ((info.m_nFlags & FIRE_BULLETS_DONT_HIT_UNDERWATER) == 0) )
  1809. {
  1810. // Damage specified by function parameter
  1811. CTakeDamageInfo dmgInfo( this, pAttacker, flActualDamage, nActualDamageType );
  1812. CalculateBulletDamageForce( &dmgInfo, info.m_iAmmoType, vecDir, tr.endpos );
  1813. dmgInfo.ScaleDamageForce( info.m_flDamageForceScale );
  1814. dmgInfo.SetAmmoType( info.m_iAmmoType );
  1815. tr.m_pEnt->DispatchTraceAttack( dmgInfo, vecDir, &tr );
  1816. if ( ToBaseCombatCharacter( tr.m_pEnt ) )
  1817. {
  1818. flCumulativeDamage += dmgInfo.GetDamage();
  1819. }
  1820. if ( bStartedInWater || !bHitWater || (info.m_nFlags & FIRE_BULLETS_ALLOW_WATER_SURFACE_IMPACTS) )
  1821. {
  1822. if ( bDoServerEffects == true )
  1823. {
  1824. DoImpactEffect( tr, nDamageType );
  1825. }
  1826. else
  1827. {
  1828. bDoImpacts = true;
  1829. }
  1830. }
  1831. else
  1832. {
  1833. // We may not impact, but we DO need to affect ragdolls on the client
  1834. CEffectData data;
  1835. data.m_vStart = tr.startpos;
  1836. data.m_vOrigin = tr.endpos;
  1837. data.m_nDamageType = nDamageType;
  1838. DispatchEffect( "RagdollImpact", data );
  1839. }
  1840. #ifdef GAME_DLL
  1841. if ( nAmmoFlags & AMMO_FORCE_DROP_IF_CARRIED )
  1842. {
  1843. // Make sure if the player is holding this, he drops it
  1844. Pickup_ForcePlayerToDropThisObject( tr.m_pEnt );
  1845. }
  1846. #endif
  1847. }
  1848. }
  1849. // See if we hit glass
  1850. if ( tr.m_pEnt != NULL )
  1851. {
  1852. #ifdef GAME_DLL
  1853. surfacedata_t *psurf = physprops->GetSurfaceData( tr.surface.surfaceProps );
  1854. if ( ( psurf != NULL ) && ( psurf->game.material == CHAR_TEX_GLASS ) && ( tr.m_pEnt->ClassMatches( "func_breakable" ) ) )
  1855. {
  1856. // Query the func_breakable for whether it wants to allow for bullet penetration
  1857. if ( tr.m_pEnt->HasSpawnFlags( SF_BREAK_NO_BULLET_PENETRATION ) == false )
  1858. {
  1859. bHitGlass = true;
  1860. }
  1861. }
  1862. #endif
  1863. }
  1864. if ( ( info.m_iTracerFreq != 0 ) && ( tracerCount++ % info.m_iTracerFreq ) == 0 && ( bHitGlass == false ) )
  1865. {
  1866. if ( bDoServerEffects == true )
  1867. {
  1868. Vector vecTracerSrc = vec3_origin;
  1869. ComputeTracerStartPosition( info.m_vecSrc, &vecTracerSrc );
  1870. trace_t Tracer;
  1871. Tracer = tr;
  1872. Tracer.endpos = vecTracerDest;
  1873. #ifdef PORTAL
  1874. if ( pShootThroughPortal )
  1875. {
  1876. Tracer.endpos = info.m_vecSrc + ( vecEnd - info.m_vecSrc ) * fPortalFraction;
  1877. }
  1878. #endif //#ifdef PORTAL
  1879. MakeTracer( vecTracerSrc, Tracer, pAmmoDef->TracerType(info.m_iAmmoType) );
  1880. #ifdef PORTAL
  1881. if ( pShootThroughPortal )
  1882. {
  1883. Vector vTransformedIntersection;
  1884. UTIL_Portal_PointTransform( pShootThroughPortal->MatrixThisToLinked(), Tracer.endpos, vTransformedIntersection );
  1885. ComputeTracerStartPosition( vTransformedIntersection, &vecTracerSrc );
  1886. Tracer.endpos = vecTracerDest;
  1887. MakeTracer( vecTracerSrc, Tracer, pAmmoDef->TracerType(info.m_iAmmoType) );
  1888. // Shooting through a portal, the damage direction is translated through the passed-through portal
  1889. // so the damage indicator hud animation is correct
  1890. Vector vDmgOriginThroughPortal;
  1891. UTIL_Portal_PointTransform( pShootThroughPortal->MatrixThisToLinked(), info.m_vecSrc, vDmgOriginThroughPortal );
  1892. g_MultiDamage.SetDamagePosition ( vDmgOriginThroughPortal );
  1893. }
  1894. else
  1895. {
  1896. g_MultiDamage.SetDamagePosition ( info.m_vecSrc );
  1897. }
  1898. #endif //#ifdef PORTAL
  1899. }
  1900. else
  1901. {
  1902. bDoTracers = true;
  1903. }
  1904. }
  1905. //NOTENOTE: We could expand this to a more general solution for various material penetration types (wood, thin metal, etc)
  1906. // See if we should pass through glass
  1907. #ifdef GAME_DLL
  1908. if ( bHitGlass )
  1909. {
  1910. HandleShotImpactingGlass( info, tr, vecDir, &traceFilter );
  1911. }
  1912. #endif
  1913. iSeed++;
  1914. }
  1915. #ifdef GAME_DLL
  1916. ApplyMultiDamage();
  1917. if ( IsPlayer() && flCumulativeDamage > 0.0f )
  1918. {
  1919. #ifndef _GAMECONSOLE
  1920. CTakeDamageInfo dmgInfo( this, pAttacker, flCumulativeDamage, nDamageType );
  1921. CBasePlayer *pPlayer = static_cast< CBasePlayer * >( this );
  1922. gamestats->Event_WeaponHit( pPlayer, info.m_bPrimaryAttack, pPlayer->GetActiveWeapon()->GetClassname(), dmgInfo );
  1923. #endif
  1924. }
  1925. #endif
  1926. }
  1927. //-----------------------------------------------------------------------------
  1928. // Should we draw bubbles underwater?
  1929. //-----------------------------------------------------------------------------
  1930. bool CBaseEntity::ShouldDrawUnderwaterBulletBubbles()
  1931. {
  1932. #if defined( HL2_DLL ) && defined( GAME_DLL )
  1933. CBaseEntity *pPlayer = ( gpGlobals->maxClients == 1 ) ? UTIL_GetLocalPlayer() : NULL;
  1934. return pPlayer && (pPlayer->GetWaterLevel() == WL_Eyes);
  1935. #else
  1936. return false;
  1937. #endif
  1938. }
  1939. //-----------------------------------------------------------------------------
  1940. // Handle shot entering water
  1941. //-----------------------------------------------------------------------------
  1942. bool CBaseEntity::HandleShotImpactingWater( const FireBulletsInfo_t &info,
  1943. const Vector &vecEnd, ITraceFilter *pTraceFilter, Vector *pVecTracerDest )
  1944. {
  1945. trace_t waterTrace;
  1946. // Trace again with water enabled
  1947. AI_TraceLine( info.m_vecSrc, vecEnd, (MASK_SHOT|CONTENTS_WATER|CONTENTS_SLIME), pTraceFilter, &waterTrace );
  1948. // See if this is the point we entered
  1949. if ( ( enginetrace->GetPointContents( waterTrace.endpos - Vector(0,0,0.1f), MASK_WATER ) & (CONTENTS_WATER|CONTENTS_SLIME) ) == 0 )
  1950. return false;
  1951. if ( ShouldDrawWaterImpacts() )
  1952. {
  1953. int nMinSplashSize = GetAmmoDef()->MinSplashSize(info.m_iAmmoType);
  1954. int nMaxSplashSize = GetAmmoDef()->MaxSplashSize(info.m_iAmmoType);
  1955. CEffectData data;
  1956. data.m_vOrigin = waterTrace.endpos;
  1957. data.m_vNormal = waterTrace.plane.normal;
  1958. data.m_flScale = random->RandomFloat( nMinSplashSize, nMaxSplashSize );
  1959. if ( waterTrace.contents & CONTENTS_SLIME )
  1960. {
  1961. data.m_fFlags |= FX_WATER_IN_SLIME;
  1962. }
  1963. DispatchEffect( "gunshotsplash", data );
  1964. }
  1965. #ifdef GAME_DLL
  1966. if ( ShouldDrawUnderwaterBulletBubbles() )
  1967. {
  1968. CWaterBullet *pWaterBullet = ( CWaterBullet * )CreateEntityByName( "waterbullet" );
  1969. if ( pWaterBullet )
  1970. {
  1971. pWaterBullet->Spawn( waterTrace.endpos, info.m_vecDirShooting );
  1972. CEffectData tracerData;
  1973. tracerData.m_vStart = waterTrace.endpos;
  1974. tracerData.m_vOrigin = waterTrace.endpos + info.m_vecDirShooting * 400.0f;
  1975. tracerData.m_fFlags = TRACER_TYPE_WATERBULLET;
  1976. DispatchEffect( "TracerSound", tracerData );
  1977. }
  1978. }
  1979. #endif
  1980. *pVecTracerDest = waterTrace.endpos;
  1981. return true;
  1982. }
  1983. ITraceFilter* CBaseEntity::GetBeamTraceFilter( void )
  1984. {
  1985. return NULL;
  1986. }
  1987. void CBaseEntity::DispatchTraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
  1988. {
  1989. #ifdef GAME_DLL
  1990. // Make sure our damage filter allows the damage.
  1991. if ( !PassesDamageFilter( info ))
  1992. {
  1993. return;
  1994. }
  1995. #endif
  1996. TraceAttack( info, vecDir, ptr );
  1997. }
  1998. void CBaseEntity::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
  1999. {
  2000. Vector vecOrigin = ptr->endpos - vecDir * 4;
  2001. if ( m_takedamage )
  2002. {
  2003. AddMultiDamage( info, this );
  2004. int blood = BloodColor();
  2005. if ( blood != DONT_BLEED )
  2006. {
  2007. SpawnBlood( vecOrigin, vecDir, blood, info.GetDamage() );// a little surface blood.
  2008. TraceBleed( info.GetDamage(), vecDir, ptr, info.GetDamageType() );
  2009. }
  2010. }
  2011. }
  2012. //-----------------------------------------------------------------------------
  2013. // Allows the shooter to change the impact effect of his bullets
  2014. //-----------------------------------------------------------------------------
  2015. void CBaseEntity::DoImpactEffect( trace_t &tr, int nDamageType )
  2016. {
  2017. // give shooter a chance to do a custom impact.
  2018. UTIL_ImpactTrace( &tr, nDamageType );
  2019. }
  2020. //-----------------------------------------------------------------------------
  2021. // Computes the tracer start position
  2022. //-----------------------------------------------------------------------------
  2023. void CBaseEntity::ComputeTracerStartPosition( const Vector &vecShotSrc, Vector *pVecTracerStart )
  2024. {
  2025. if ( g_pGameRules->IsMultiplayer() )
  2026. {
  2027. // NOTE: we do this because in MakeTracer, we force it to use the attachment position
  2028. // in multiplayer, so the results from this function should never actually get used.
  2029. pVecTracerStart->Init( 999, 999, 999 );
  2030. return;
  2031. }
  2032. if ( IsPlayer() )
  2033. {
  2034. // adjust tracer position for player
  2035. Vector forward, right;
  2036. CBasePlayer *pPlayer = ToBasePlayer( this );
  2037. pPlayer->EyeVectors( &forward, &right, NULL );
  2038. *pVecTracerStart = vecShotSrc + Vector ( 0 , 0 , -4 ) + right * 2 + forward * 16;
  2039. }
  2040. else
  2041. {
  2042. *pVecTracerStart = vecShotSrc;
  2043. CBaseCombatCharacter *pBCC = MyCombatCharacterPointer();
  2044. if ( pBCC != NULL )
  2045. {
  2046. CBaseCombatWeapon *pWeapon = pBCC->GetActiveWeapon();
  2047. if ( pWeapon != NULL )
  2048. {
  2049. Vector vecMuzzle;
  2050. QAngle vecMuzzleAngles;
  2051. if ( pWeapon->GetAttachment( 1, vecMuzzle, vecMuzzleAngles ) )
  2052. {
  2053. *pVecTracerStart = vecMuzzle;
  2054. }
  2055. }
  2056. }
  2057. }
  2058. }
  2059. //-----------------------------------------------------------------------------
  2060. // Purpose: Virtual function allows entities to handle tracer presentation
  2061. // as they see fit.
  2062. //
  2063. // Input : vecTracerSrc - the point at which to start the tracer (not always the
  2064. // same spot as the traceline!
  2065. //
  2066. // tr - the entire trace result for the shot.
  2067. //
  2068. // Output :
  2069. //-----------------------------------------------------------------------------
  2070. void CBaseEntity::MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType )
  2071. {
  2072. const char *pszTracerName = GetTracerType();
  2073. Vector vNewSrc = vecTracerSrc;
  2074. int iAttachment = GetTracerAttachment();
  2075. switch ( iTracerType )
  2076. {
  2077. case TRACER_LINE:
  2078. UTIL_Tracer( vNewSrc, tr.endpos, entindex(), iAttachment, 0.0f, false, pszTracerName );
  2079. break;
  2080. case TRACER_LINE_AND_WHIZ:
  2081. UTIL_Tracer( vNewSrc, tr.endpos, entindex(), iAttachment, 0.0f, true, pszTracerName );
  2082. break;
  2083. }
  2084. }
  2085. //-----------------------------------------------------------------------------
  2086. // Default tracer attachment
  2087. //-----------------------------------------------------------------------------
  2088. int CBaseEntity::GetTracerAttachment( void )
  2089. {
  2090. int iAttachment = TRACER_DONT_USE_ATTACHMENT;
  2091. if ( g_pGameRules->IsMultiplayer() )
  2092. {
  2093. iAttachment = 1;
  2094. }
  2095. return iAttachment;
  2096. }
  2097. int CBaseEntity::BloodColor()
  2098. {
  2099. return DONT_BLEED;
  2100. }
  2101. void CBaseEntity::TraceBleed( float flDamage, const Vector &vecDir, trace_t *ptr, int bitsDamageType )
  2102. {
  2103. if ((BloodColor() == DONT_BLEED) || (BloodColor() == BLOOD_COLOR_MECH))
  2104. {
  2105. return;
  2106. }
  2107. if (flDamage == 0)
  2108. return;
  2109. if (! (bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_AIRBOAT)))
  2110. return;
  2111. // make blood decal on the wall!
  2112. trace_t Bloodtr;
  2113. Vector vecTraceDir;
  2114. float flNoise;
  2115. int cCount;
  2116. int i;
  2117. #ifdef GAME_DLL
  2118. if ( !IsAlive() )
  2119. {
  2120. // dealing with a dead npc.
  2121. if ( GetMaxHealth() <= 0 )
  2122. {
  2123. // no blood decal for a npc that has already decalled its limit.
  2124. return;
  2125. }
  2126. else
  2127. {
  2128. m_iMaxHealth -= 1;
  2129. }
  2130. }
  2131. #endif
  2132. if (flDamage < 10)
  2133. {
  2134. flNoise = 0.1;
  2135. cCount = 1;
  2136. }
  2137. else if (flDamage < 25)
  2138. {
  2139. flNoise = 0.2;
  2140. cCount = 2;
  2141. }
  2142. else
  2143. {
  2144. flNoise = 0.3;
  2145. cCount = 4;
  2146. }
  2147. float flTraceDist = (bitsDamageType & DMG_AIRBOAT) ? 384 : 172;
  2148. for ( i = 0 ; i < cCount ; i++ )
  2149. {
  2150. vecTraceDir = vecDir * -1;// trace in the opposite direction the shot came from (the direction the shot is going)
  2151. vecTraceDir.x += random->RandomFloat( -flNoise, flNoise );
  2152. vecTraceDir.y += random->RandomFloat( -flNoise, flNoise );
  2153. vecTraceDir.z += random->RandomFloat( -flNoise, flNoise );
  2154. // Don't bleed on grates.
  2155. AI_TraceLine( ptr->endpos, ptr->endpos + vecTraceDir * -flTraceDist, MASK_SOLID_BRUSHONLY & ~CONTENTS_GRATE, this, COLLISION_GROUP_NONE, &Bloodtr);
  2156. if ( Bloodtr.fraction != 1.0 )
  2157. {
  2158. UTIL_BloodDecalTrace( &Bloodtr, BloodColor() );
  2159. }
  2160. }
  2161. }
  2162. const char* CBaseEntity::GetTracerType()
  2163. {
  2164. return NULL;
  2165. }
  2166. //-----------------------------------------------------------------------------
  2167. // These methods encapsulate MOVETYPE_FOLLOW, which became obsolete
  2168. //-----------------------------------------------------------------------------
  2169. void CBaseEntity::FollowEntity( CBaseEntity *pBaseEntity, bool bBoneMerge )
  2170. {
  2171. if (pBaseEntity)
  2172. {
  2173. SetParent( pBaseEntity );
  2174. SetMoveType( MOVETYPE_NONE );
  2175. if ( bBoneMerge )
  2176. AddEffects( EF_BONEMERGE );
  2177. AddSolidFlags( FSOLID_NOT_SOLID );
  2178. SetLocalOrigin( vec3_origin );
  2179. SetLocalAngles( vec3_angle );
  2180. }
  2181. else
  2182. {
  2183. StopFollowingEntity();
  2184. }
  2185. }
  2186. void CBaseEntity::SetEffectEntity( CBaseEntity *pEffectEnt )
  2187. {
  2188. if ( m_hEffectEntity.Get() != pEffectEnt )
  2189. {
  2190. m_hEffectEntity = pEffectEnt;
  2191. }
  2192. }
  2193. void CBaseEntity::ApplyLocalVelocityImpulse( const Vector &inVecImpulse )
  2194. {
  2195. // NOTE: Don't have to use GetVelocity here because local values
  2196. // are always guaranteed to be correct, unlike abs values which may
  2197. // require recomputation
  2198. if ( inVecImpulse != vec3_origin )
  2199. {
  2200. Vector vecImpulse = inVecImpulse;
  2201. // Safety check against receive a huge impulse, which can explode physics
  2202. switch ( CheckEntityVelocity( vecImpulse ) )
  2203. {
  2204. case -1:
  2205. Warning( "Discarding ApplyLocalVelocityImpulse(%f,%f,%f) on %s\n", vecImpulse.x, vecImpulse.y, vecImpulse.z, GetDebugName() );
  2206. Assert( false );
  2207. return;
  2208. case 0:
  2209. if ( CheckEmitReasonablePhysicsSpew() )
  2210. {
  2211. Warning( "Bad ApplyLocalVelocityImpulse(%f,%f,%f) on %s\n", vecImpulse.x, vecImpulse.y, vecImpulse.z, GetDebugName() );
  2212. }
  2213. Assert( false );
  2214. break;
  2215. default:
  2216. break;
  2217. };
  2218. if ( GetMoveType() == MOVETYPE_VPHYSICS )
  2219. {
  2220. IPhysicsObject *ppPhysObjs[ VPHYSICS_MAX_OBJECT_LIST_COUNT ];
  2221. int nNumPhysObjs = VPhysicsGetObjectList( ppPhysObjs, VPHYSICS_MAX_OBJECT_LIST_COUNT );
  2222. for ( int i = 0; i < nNumPhysObjs; i++ )
  2223. {
  2224. Vector worldVel;
  2225. ppPhysObjs[ i ]->LocalToWorld( &worldVel, vecImpulse );
  2226. ppPhysObjs[ i ]->AddVelocity( &worldVel, NULL );
  2227. }
  2228. }
  2229. else
  2230. {
  2231. InvalidatePhysicsRecursive( VELOCITY_CHANGED );
  2232. m_vecVelocity += vecImpulse;
  2233. }
  2234. }
  2235. }
  2236. void CBaseEntity::ApplyAbsVelocityImpulse( const Vector &inVecImpulse )
  2237. {
  2238. if (inVecImpulse != vec3_origin )
  2239. {
  2240. Vector vecImpulse = inVecImpulse;
  2241. // Safety check against receive a huge impulse, which can explode physics
  2242. switch ( CheckEntityVelocity( vecImpulse ) )
  2243. {
  2244. case -1:
  2245. Warning( "Discarding ApplyAbsVelocityImpulse(%f,%f,%f) on %s\n", vecImpulse.x, vecImpulse.y, vecImpulse.z, GetDebugName() );
  2246. Assert( false );
  2247. return;
  2248. case 0:
  2249. if ( CheckEmitReasonablePhysicsSpew() )
  2250. {
  2251. Warning( "Bad ApplyAbsVelocityImpulse(%f,%f,%f) on %s\n", vecImpulse.x, vecImpulse.y, vecImpulse.z, GetDebugName() );
  2252. }
  2253. Assert( false );
  2254. return;
  2255. default:
  2256. break;
  2257. }
  2258. if ( GetMoveType() == MOVETYPE_VPHYSICS )
  2259. {
  2260. IPhysicsObject *ppPhysObjs[ VPHYSICS_MAX_OBJECT_LIST_COUNT ];
  2261. int nNumPhysObjs = VPhysicsGetObjectList( ppPhysObjs, VPHYSICS_MAX_OBJECT_LIST_COUNT );
  2262. for ( int i = 0; i < nNumPhysObjs; i++ )
  2263. {
  2264. ppPhysObjs[ i ]->AddVelocity( &vecImpulse, NULL );
  2265. }
  2266. }
  2267. else
  2268. {
  2269. // NOTE: Have to use GetAbsVelocity here to ensure it's the correct value
  2270. Vector vecResult;
  2271. VectorAdd( GetAbsVelocity(), vecImpulse, vecResult );
  2272. SetAbsVelocity( vecResult );
  2273. }
  2274. }
  2275. }
  2276. void CBaseEntity::ApplyLocalAngularVelocityImpulse( const AngularImpulse &angImpulse )
  2277. {
  2278. if (angImpulse != vec3_origin )
  2279. {
  2280. // Safety check against receive a huge impulse, which can explode physics
  2281. if ( !IsEntityAngularVelocityReasonable( angImpulse ) )
  2282. {
  2283. if ( CheckEmitReasonablePhysicsSpew() )
  2284. {
  2285. Warning( "Bad ApplyLocalAngularVelocityImpulse(%f,%f,%f) on %s\n", angImpulse.x, angImpulse.y, angImpulse.z, GetDebugName() );
  2286. }
  2287. Assert( false );
  2288. return;
  2289. }
  2290. if ( GetMoveType() == MOVETYPE_VPHYSICS )
  2291. {
  2292. IPhysicsObject *ppPhysObjs[ VPHYSICS_MAX_OBJECT_LIST_COUNT ];
  2293. int nNumPhysObjs = VPhysicsGetObjectList( ppPhysObjs, VPHYSICS_MAX_OBJECT_LIST_COUNT );
  2294. for ( int i = 0; i < nNumPhysObjs; i++ )
  2295. {
  2296. ppPhysObjs[ i ]->AddVelocity( NULL, &angImpulse );
  2297. }
  2298. }
  2299. else
  2300. {
  2301. QAngle vecResult;
  2302. AngularImpulseToQAngle( angImpulse, vecResult );
  2303. VectorAdd( GetLocalAngularVelocity(), vecResult, vecResult );
  2304. SetLocalAngularVelocity( vecResult );
  2305. }
  2306. }
  2307. }
  2308. void CBaseEntity::SetCollisionGroup( int collisionGroup )
  2309. {
  2310. if ( (int)m_CollisionGroup != collisionGroup )
  2311. {
  2312. m_CollisionGroup = collisionGroup;
  2313. CollisionRulesChanged();
  2314. }
  2315. }
  2316. void CBaseEntity::CollisionRulesChanged()
  2317. {
  2318. // ivp maintains state based on recent return values from the collision filter, so anything
  2319. // that can change the state that a collision filter will return (like m_Solid) needs to call RecheckCollisionFilter.
  2320. if ( VPhysicsGetObject() )
  2321. {
  2322. extern bool PhysIsInCallback();
  2323. if ( PhysIsInCallback() )
  2324. {
  2325. Warning("Changing collision rules within a callback is likely to cause crashes!\n");
  2326. Assert(0);
  2327. }
  2328. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  2329. int count = VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  2330. for ( int i = 0; i < count; i++ )
  2331. {
  2332. if ( pList[i] != NULL ) //this really shouldn't happen, but it does >_<
  2333. pList[i]->RecheckCollisionFilter();
  2334. }
  2335. }
  2336. }
  2337. int CBaseEntity::GetWaterType() const
  2338. {
  2339. int out = 0;
  2340. if ( m_nWaterType & 1 )
  2341. out |= CONTENTS_WATER;
  2342. if ( m_nWaterType & 2 )
  2343. out |= CONTENTS_SLIME;
  2344. return out;
  2345. }
  2346. void CBaseEntity::SetWaterType( int nType )
  2347. {
  2348. m_nWaterType = 0;
  2349. if ( nType & CONTENTS_WATER )
  2350. m_nWaterType |= 1;
  2351. if ( nType & CONTENTS_SLIME )
  2352. m_nWaterType |= 2;
  2353. }
  2354. //-----------------------------------------------------------------------------
  2355. // Purpose:
  2356. // Output : Returns true on success, false on failure.
  2357. //-----------------------------------------------------------------------------
  2358. bool CBaseEntity::IsSimulatingOnAlternateTicks()
  2359. {
  2360. if ( gpGlobals->maxClients != 1 )
  2361. {
  2362. return false;
  2363. }
  2364. static ConVarRef sv_alternateticks( "sv_alternateticks" );
  2365. if ( sv_alternateticks.IsValid() )
  2366. {
  2367. return sv_alternateticks.GetBool();
  2368. }
  2369. else
  2370. {
  2371. return IsX360();
  2372. }
  2373. }
  2374. #ifdef CLIENT_DLL
  2375. //-----------------------------------------------------------------------------
  2376. // Purpose:
  2377. // Input : -
  2378. // Output : Returns true on success, false on failure.
  2379. //-----------------------------------------------------------------------------
  2380. bool CBaseEntity::IsToolRecording() const
  2381. {
  2382. #ifndef NO_TOOLFRAMEWORK
  2383. return m_bToolRecording;
  2384. #else
  2385. return false;
  2386. #endif
  2387. }
  2388. #endif
  2389. #if defined( CLIENT_DLL ) && !defined( PORTAL2 )
  2390. #define FAST_TRIGGER_TOUCH
  2391. extern void TouchTriggerPlayerMovement( C_BaseEntity *pEntity );
  2392. #endif
  2393. void CBaseEntity::PhysicsTouchTriggers( const Vector *pPrevAbsOrigin )
  2394. {
  2395. #if defined( CLIENT_DLL )
  2396. #if defined( FAST_TRIGGER_TOUCH )
  2397. {
  2398. Assert( !pPrevAbsOrigin );
  2399. TouchTriggerPlayerMovement( this );
  2400. return;
  2401. }
  2402. #endif // FAST_TRIGGER_TOUCH
  2403. IClientEntity *pEntity = this;
  2404. #else
  2405. edict_t *pEntity = edict();
  2406. #endif
  2407. if ( pEntity && !IsWorld() )
  2408. {
  2409. Assert(CollisionProp());
  2410. bool isTriggerCheckSolids = IsSolidFlagSet( FSOLID_TRIGGER );
  2411. bool isSolidCheckTriggers = IsSolid() && !isTriggerCheckSolids; // NOTE: Moving triggers (items, ammo etc) are not
  2412. // checked against other triggers ot reduce the number of touchlinks created
  2413. if ( !(isSolidCheckTriggers || isTriggerCheckSolids) )
  2414. return;
  2415. if ( GetSolid() == SOLID_BSP )
  2416. {
  2417. if ( !GetModel() && Q_strlen( STRING( GetModelName() ) ) == 0 )
  2418. {
  2419. Warning( "Inserted %s with no model\n", GetClassname() );
  2420. return;
  2421. }
  2422. }
  2423. SetCheckUntouch( true );
  2424. if ( isSolidCheckTriggers )
  2425. {
  2426. engine->SolidMoved( pEntity, CollisionProp(), pPrevAbsOrigin, sm_bAccurateTriggerBboxChecks );
  2427. }
  2428. if ( isTriggerCheckSolids )
  2429. {
  2430. engine->TriggerMoved( pEntity, sm_bAccurateTriggerBboxChecks );
  2431. }
  2432. }
  2433. }
  2434. void CBaseEntity::UpdateLastMadeNoiseTime( const char* pszSoundName /*= NULL */ )
  2435. {
  2436. m_flLastMadeNoiseTime = gpGlobals->curtime;
  2437. }