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.

2277 lines
68 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "cbase.h"
  7. #include "mathlib/mathlib.h"
  8. #include "util_shared.h"
  9. #include "model_types.h"
  10. #include "convar.h"
  11. #include "IEffects.h"
  12. #include "vphysics/object_hash.h"
  13. #include "mathlib/IceKey.H"
  14. #include "checksum_crc.h"
  15. #include "particle_parse.h"
  16. #include "keyvalues.h"
  17. #include "icommandline.h"
  18. #ifdef CLIENT_DLL
  19. #include "clientleafsystem.h"
  20. #include "c_te_effect_dispatch.h"
  21. #else
  22. #include "te_effect_dispatch.h"
  23. bool NPC_CheckBrushExclude( CBaseEntity *pEntity, CBaseEntity *pBrush );
  24. #endif
  25. // memdbgon must be the last include file in a .cpp file!!!
  26. #include "tier0/memdbgon.h"
  27. ConVar r_visualizetraces( "r_visualizetraces", "0", FCVAR_CHEAT );
  28. ConVar developer("developer", "0", FCVAR_RELEASE, "Set developer message level" ); // developer mode
  29. #ifdef DETECT_TRACE_SPIKES
  30. float g_TraceSpikeTolerance = 0.25;
  31. ConVar trace_spike_tolerance( "trace_spike_tolerance", "0.25" );
  32. void DoReportExpensiveTrace( bool repeat, float time )
  33. {
  34. if ( g_TraceSpikeTolerance > 0.0f )
  35. {
  36. Msg( "%s%f!\n", ( repeat ) ? " R: " : "", time );
  37. }
  38. g_TraceSpikeTolerance = trace_spike_tolerance.GetFloat();
  39. }
  40. #endif
  41. float UTIL_VecToYaw( const Vector &vec )
  42. {
  43. if (vec.y == 0 && vec.x == 0)
  44. return 0;
  45. float yaw = atan2( vec.y, vec.x );
  46. yaw = RAD2DEG(yaw);
  47. if (yaw < 0)
  48. yaw += 360;
  49. return yaw;
  50. }
  51. float UTIL_VecToPitch( const Vector &vec )
  52. {
  53. float pitch = 0;
  54. Vector tmp = vec;
  55. if ( VectorNormalize( tmp ) > 0 )
  56. {
  57. pitch = RAD2DEG( asin( -tmp.z ) );
  58. }
  59. return pitch;
  60. }
  61. float UTIL_VecToYaw( const matrix3x4_t &matrix, const Vector &vec )
  62. {
  63. Vector tmp = vec;
  64. VectorNormalize( tmp );
  65. float x = matrix[0][0] * tmp.x + matrix[1][0] * tmp.y + matrix[2][0] * tmp.z;
  66. float y = matrix[0][1] * tmp.x + matrix[1][1] * tmp.y + matrix[2][1] * tmp.z;
  67. if (x == 0.0f && y == 0.0f)
  68. return 0.0f;
  69. float yaw = atan2( -y, x );
  70. yaw = RAD2DEG(yaw);
  71. if (yaw < 0)
  72. yaw += 360;
  73. return yaw;
  74. }
  75. float UTIL_VecToPitch( const matrix3x4_t &matrix, const Vector &vec )
  76. {
  77. float pitch = 0;
  78. Vector tmp = vec;
  79. if ( VectorNormalize( tmp ) > 0 )
  80. {
  81. float z = matrix[0][2] * tmp.x + matrix[1][2] * tmp.y + matrix[2][2] * tmp.z;
  82. pitch = RAD2DEG( asin( -z ) );
  83. if (pitch < 0)
  84. pitch += 360;
  85. }
  86. return pitch;
  87. }
  88. Vector UTIL_YawToVector( float yaw )
  89. {
  90. Vector ret;
  91. ret.z = 0;
  92. float angle = DEG2RAD( yaw );
  93. SinCos( angle, &ret.y, &ret.x );
  94. return ret;
  95. }
  96. //-----------------------------------------------------------------------------
  97. // Purpose: Helper function get get determinisitc random values for shared/prediction code
  98. // Input : seedvalue -
  99. // *module -
  100. // line -
  101. // Output : static int
  102. //-----------------------------------------------------------------------------
  103. static int SeedFileLineHash( int seedvalue, const char *sharedname, int additionalSeed )
  104. {
  105. CRC32_t retval;
  106. CRC32_Init( &retval );
  107. //ensure cross-platform agreement
  108. seedvalue = LittleDWord( seedvalue );
  109. additionalSeed = LittleDWord( additionalSeed );
  110. CRC32_ProcessBuffer( &retval, (void *)&seedvalue, sizeof( int ) );
  111. CRC32_ProcessBuffer( &retval, (void *)&additionalSeed, sizeof( int ) );
  112. CRC32_ProcessBuffer( &retval, (void *)sharedname, Q_strlen( sharedname ) );
  113. CRC32_Final( &retval );
  114. return (int)( retval );
  115. }
  116. float SharedRandomFloat( const char *sharedname, float flMinVal, float flMaxVal, int additionalSeed /*=0*/ )
  117. {
  118. Assert( CBaseEntity::GetPredictionRandomSeed() != -1 );
  119. int seed = SeedFileLineHash( CBaseEntity::GetPredictionRandomSeed(), sharedname, additionalSeed );
  120. RandomSeed( seed );
  121. return RandomFloat( flMinVal, flMaxVal );
  122. }
  123. int SharedRandomInt( const char *sharedname, int iMinVal, int iMaxVal, int additionalSeed /*=0*/ )
  124. {
  125. Assert( CBaseEntity::GetPredictionRandomSeed() != -1 );
  126. int seed = SeedFileLineHash( CBaseEntity::GetPredictionRandomSeed(), sharedname, additionalSeed );
  127. RandomSeed( seed );
  128. return RandomInt( iMinVal, iMaxVal );
  129. }
  130. Vector SharedRandomVector( const char *sharedname, float minVal, float maxVal, int additionalSeed /*=0*/ )
  131. {
  132. Assert( CBaseEntity::GetPredictionRandomSeed() != -1 );
  133. int seed = SeedFileLineHash( CBaseEntity::GetPredictionRandomSeed(), sharedname, additionalSeed );
  134. RandomSeed( seed );
  135. // HACK: Can't call RandomVector/Angle because it uses rand() not vstlib Random*() functions!
  136. // Get a random vector.
  137. Vector random;
  138. random.x = RandomFloat( minVal, maxVal );
  139. random.y = RandomFloat( minVal, maxVal );
  140. random.z = RandomFloat( minVal, maxVal );
  141. return random;
  142. }
  143. QAngle SharedRandomAngle( const char *sharedname, float minVal, float maxVal, int additionalSeed /*=0*/ )
  144. {
  145. Assert( CBaseEntity::GetPredictionRandomSeed() != -1 );
  146. int seed = SeedFileLineHash( CBaseEntity::GetPredictionRandomSeed(), sharedname, additionalSeed );
  147. RandomSeed( seed );
  148. // HACK: Can't call RandomVector/Angle because it uses rand() not vstlib Random*() functions!
  149. // Get a random vector.
  150. Vector random;
  151. random.x = RandomFloat( minVal, maxVal );
  152. random.y = RandomFloat( minVal, maxVal );
  153. random.z = RandomFloat( minVal, maxVal );
  154. return QAngle( random.x, random.y, random.z );
  155. }
  156. //-----------------------------------------------------------------------------
  157. //
  158. // Shared client/server trace filter code
  159. //
  160. //-----------------------------------------------------------------------------
  161. bool PassServerEntityFilter( const IHandleEntity *pTouch, const IHandleEntity *pPass )
  162. {
  163. if ( !pPass )
  164. return true;
  165. if ( pTouch == pPass )
  166. return false;
  167. const CBaseEntity *pEntTouch = EntityFromEntityHandle( pTouch );
  168. const CBaseEntity *pEntPass = EntityFromEntityHandle( pPass );
  169. if ( !pEntTouch || !pEntPass )
  170. return true;
  171. // don't clip against own missiles
  172. if ( pEntTouch->GetOwnerEntity() == pEntPass )
  173. return false;
  174. // don't clip against owner
  175. if ( pEntPass->GetOwnerEntity() == pEntTouch )
  176. return false;
  177. return true;
  178. }
  179. //-----------------------------------------------------------------------------
  180. // A standard filter to be applied to just about everything.
  181. //-----------------------------------------------------------------------------
  182. bool StandardFilterRules( IHandleEntity *pHandleEntity, int fContentsMask )
  183. {
  184. CBaseEntity *pCollide = EntityFromEntityHandle( pHandleEntity );
  185. // Static prop case...
  186. if ( !pCollide )
  187. return true;
  188. SolidType_t solid = pCollide->GetSolid();
  189. const model_t *pModel = pCollide->GetModel();
  190. if ( ( modelinfo->GetModelType( pModel ) != mod_brush ) || (solid != SOLID_BSP && solid != SOLID_VPHYSICS) )
  191. {
  192. if ( (fContentsMask & CONTENTS_MONSTER) == 0 )
  193. return false;
  194. }
  195. // This code is used to cull out tests against see-thru entities
  196. if ( !(fContentsMask & CONTENTS_WINDOW) )
  197. {
  198. #ifdef CLIENT_DLL
  199. ClientRenderHandle_t hRender = pCollide->RenderHandle();
  200. if ( hRender == INVALID_CLIENT_RENDER_HANDLE )
  201. {
  202. // this is to handle invisible owned entities (e.g. bone followers) with transparent owners
  203. // not being filtered properly. This is kind of a hack but seems to provide the behavior we
  204. // want in all cases - if you are owned by something transparent and you yourself are not visible
  205. // we filter based on your owner's transparency
  206. CBaseEntity *pOwner = pCollide->GetOwnerEntity();
  207. if ( pOwner )
  208. {
  209. hRender = pOwner->RenderHandle();
  210. }
  211. }
  212. if ( g_pClientLeafSystem->GetTranslucencyType( hRender ) == RENDERABLE_IS_TRANSLUCENT )
  213. return false;
  214. #else
  215. bool bIsTranslucent = modelinfo->IsTranslucent( pModel );
  216. bool bIsTwoPass = modelinfo->IsTranslucentTwoPass( pModel );
  217. if ( bIsTranslucent && !bIsTwoPass )
  218. return false;
  219. #endif
  220. }
  221. // FIXME: this is to skip BSP models that are entities that can be
  222. // potentially moved/deleted, similar to a monster but doors don't seem to
  223. // be flagged as monsters
  224. // FIXME: the FL_WORLDBRUSH looked promising, but it needs to be set on
  225. // everything that's actually a worldbrush and it currently isn't
  226. if ( !(fContentsMask & CONTENTS_MOVEABLE) && (pCollide->GetMoveType() == MOVETYPE_PUSH))// !(touch->flags & FL_WORLDBRUSH) )
  227. return false;
  228. return true;
  229. }
  230. bool IsWeaponClassname( const char *pszClassName )
  231. {
  232. // Look at the name of the class to determine if this is a weapon.
  233. return ( !Q_strnicmp( "weapon_", pszClassName, WEAPON_CLASSNAME_PREFIX_LENGTH ) );
  234. }
  235. //-----------------------------------------------------------------------------
  236. // Simple trace filter
  237. //-----------------------------------------------------------------------------
  238. CTraceFilterSimple::CTraceFilterSimple( const IHandleEntity *passedict, int collisionGroup,
  239. ShouldHitFunc_t pExtraShouldHitFunc )
  240. {
  241. m_pPassEnt = passedict;
  242. m_collisionGroup = collisionGroup;
  243. m_pExtraShouldHitCheckFunction = pExtraShouldHitFunc;
  244. }
  245. //-----------------------------------------------------------------------------
  246. // The trace filter!
  247. //-----------------------------------------------------------------------------
  248. bool CTraceFilterSimple::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  249. {
  250. if ( !StandardFilterRules( pHandleEntity, contentsMask ) )
  251. return false;
  252. if ( m_pPassEnt )
  253. {
  254. if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) )
  255. {
  256. return false;
  257. }
  258. }
  259. // Don't test if the game code tells us we should ignore this collision...
  260. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  261. if ( !pEntity )
  262. return false;
  263. if ( !pEntity->ShouldCollide( m_collisionGroup, contentsMask ) )
  264. return false;
  265. if ( pEntity && !g_pGameRules->ShouldCollide( m_collisionGroup, pEntity->GetCollisionGroup() ) )
  266. return false;
  267. if ( m_pExtraShouldHitCheckFunction &&
  268. (! ( m_pExtraShouldHitCheckFunction( pHandleEntity, contentsMask ) ) ) )
  269. return false;
  270. return true;
  271. }
  272. //-----------------------------------------------------------------------------
  273. // Purpose: Trace filter that only hits NPCs and the player
  274. //-----------------------------------------------------------------------------
  275. bool CTraceFilterOnlyNPCsAndPlayer::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  276. {
  277. if ( CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask ) )
  278. {
  279. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  280. if ( !pEntity )
  281. return false;
  282. #ifdef CSTRIKE_DLL
  283. #ifndef CLIENT_DLL
  284. if ( pEntity->Classify() == CLASS_PLAYER_ALLY )
  285. return true; // CS hostages are CLASS_PLAYER_ALLY but not IsNPC()
  286. #endif // !CLIENT_DLL
  287. #endif // CSTRIKE_DLL
  288. return (pEntity->IsNPC() || pEntity->IsPlayer());
  289. }
  290. return false;
  291. }
  292. //-----------------------------------------------------------------------------
  293. // Purpose: Trace filter that doesn't hit NPCs or players
  294. //-----------------------------------------------------------------------------
  295. bool CTraceFilterNoNPCsOrPlayer::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  296. {
  297. if ( CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask ) )
  298. {
  299. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  300. if ( !pEntity )
  301. return NULL;
  302. #ifndef CLIENT_DLL
  303. if ( pEntity->Classify() == CLASS_PLAYER_ALLY )
  304. return false; // CS hostages are CLASS_PLAYER_ALLY but not IsNPC()
  305. #endif
  306. return (!pEntity->IsNPC() && !pEntity->IsPlayer());
  307. }
  308. return false;
  309. }
  310. //-----------------------------------------------------------------------------
  311. // Purpose: Trace filter that doesn't hit players
  312. //-----------------------------------------------------------------------------
  313. bool CTraceFilterNoPlayers::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  314. {
  315. if ( CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask ) )
  316. {
  317. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  318. if ( !pEntity )
  319. return NULL;
  320. return !pEntity->IsPlayer();
  321. }
  322. return false;
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Trace filter that skips two entities
  326. //-----------------------------------------------------------------------------
  327. CTraceFilterSkipTwoEntities::CTraceFilterSkipTwoEntities( const IHandleEntity *passentity, const IHandleEntity *passentity2, int collisionGroup ) :
  328. BaseClass( passentity, collisionGroup ), m_pPassEnt2(passentity2)
  329. {
  330. }
  331. bool CTraceFilterSkipTwoEntities::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  332. {
  333. Assert( pHandleEntity );
  334. if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt2 ) )
  335. return false;
  336. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  337. }
  338. //-----------------------------------------------------------------------------
  339. // Trace filter that can take a list of entities to ignore
  340. //-----------------------------------------------------------------------------
  341. CTraceFilterSimpleList::CTraceFilterSimpleList( int collisionGroup ) :
  342. CTraceFilterSimple( NULL, collisionGroup )
  343. {
  344. }
  345. //-----------------------------------------------------------------------------
  346. // Purpose:
  347. //-----------------------------------------------------------------------------
  348. bool CTraceFilterSimpleList::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  349. {
  350. if ( m_PassEntities.Find(pHandleEntity) != m_PassEntities.InvalidIndex() )
  351. return false;
  352. return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
  353. }
  354. //-----------------------------------------------------------------------------
  355. // Purpose: Add an entity to my list of entities to ignore in the trace
  356. //-----------------------------------------------------------------------------
  357. void CTraceFilterSimpleList::AddEntityToIgnore( IHandleEntity *pEntity )
  358. {
  359. m_PassEntities.AddToTail( pEntity );
  360. }
  361. void CTraceFilterSimpleList::AddEntitiesToIgnore( int nCount, IHandleEntity **ppEntities )
  362. {
  363. int nIndex = m_PassEntities.AddMultipleToTail( nCount );
  364. memcpy( &m_PassEntities[nIndex], ppEntities, nCount * sizeof( IHandleEntity* ) );
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Trace filter that hits only the pass entity
  368. //-----------------------------------------------------------------------------
  369. CTraceFilterOnlyHitThis::CTraceFilterOnlyHitThis( const IHandleEntity *hitentity )
  370. {
  371. m_pHitEnt = hitentity;
  372. }
  373. bool CTraceFilterOnlyHitThis::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  374. {
  375. return m_pHitEnt == pHandleEntity;
  376. }
  377. //-----------------------------------------------------------------------------
  378. // Purpose: Custom trace filter used for NPC LOS traces
  379. //-----------------------------------------------------------------------------
  380. CTraceFilterLOS::CTraceFilterLOS( IHandleEntity *pHandleEntity, int collisionGroup, IHandleEntity *pHandleEntity2 ) :
  381. CTraceFilterSkipTwoEntities( pHandleEntity, pHandleEntity2, collisionGroup )
  382. {
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose:
  386. //-----------------------------------------------------------------------------
  387. bool CTraceFilterLOS::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  388. {
  389. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  390. if ( !pEntity->BlocksLOS() )
  391. return false;
  392. return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
  393. }
  394. //-----------------------------------------------------------------------------
  395. // Trace filter that can take a classname to ignore
  396. //-----------------------------------------------------------------------------
  397. CTraceFilterSkipClassname::CTraceFilterSkipClassname( const IHandleEntity *passentity, const char *pchClassname, int collisionGroup ) :
  398. CTraceFilterSimple( passentity, collisionGroup ), m_pchClassname( pchClassname )
  399. {
  400. }
  401. //-----------------------------------------------------------------------------
  402. // Purpose:
  403. //-----------------------------------------------------------------------------
  404. bool CTraceFilterSkipClassname::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  405. {
  406. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  407. if ( !pEntity || FClassnameIs( pEntity, m_pchClassname ) )
  408. return false;
  409. return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Trace filter that skips two classnames
  413. //-----------------------------------------------------------------------------
  414. CTraceFilterSkipTwoClassnames::CTraceFilterSkipTwoClassnames( const IHandleEntity *passentity, const char *pchClassname, const char *pchClassname2, int collisionGroup ) :
  415. BaseClass( passentity, pchClassname, collisionGroup ), m_pchClassname2(pchClassname2)
  416. {
  417. }
  418. bool CTraceFilterSkipTwoClassnames::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  419. {
  420. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  421. if ( !pEntity || FClassnameIs( pEntity, m_pchClassname2 ) )
  422. return false;
  423. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  424. }
  425. //-----------------------------------------------------------------------------
  426. // Trace filter that can take a list of entities to ignore
  427. //-----------------------------------------------------------------------------
  428. CTraceFilterSimpleClassnameList::CTraceFilterSimpleClassnameList( const IHandleEntity *passentity, int collisionGroup ) :
  429. CTraceFilterSimple( passentity, collisionGroup )
  430. {
  431. }
  432. //-----------------------------------------------------------------------------
  433. // Purpose:
  434. //-----------------------------------------------------------------------------
  435. bool CTraceFilterSimpleClassnameList::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  436. {
  437. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  438. if ( !pEntity )
  439. return false;
  440. CBaseEntity *pOwner = pEntity->GetOwnerEntity();
  441. if ( pOwner )
  442. {
  443. const char *pOwnerClass = pOwner->GetClassname();
  444. for ( int i = 0; i < m_PassClassnames.Count(); i++ )
  445. {
  446. if ( !V_strcmp( m_PassClassnames[i], pOwnerClass ) )
  447. return false;
  448. }
  449. }
  450. const char *pEntityClass = pEntity->GetClassname();
  451. for ( int i = 0; i < m_PassClassnames.Count(); ++i )
  452. {
  453. if ( !V_strcmp( pEntityClass, m_PassClassnames[ i ] ) )
  454. return false;
  455. }
  456. return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
  457. }
  458. //-----------------------------------------------------------------------------
  459. // Purpose: Add an entity to my list of entities to ignore in the trace
  460. //-----------------------------------------------------------------------------
  461. void CTraceFilterSimpleClassnameList::AddClassnameToIgnore( const char *pchClassname )
  462. {
  463. m_PassClassnames.AddToTail( pchClassname );
  464. }
  465. CTraceFilterChain::CTraceFilterChain( ITraceFilter *pTraceFilter1, ITraceFilter *pTraceFilter2 )
  466. {
  467. m_pTraceFilter1 = pTraceFilter1;
  468. m_pTraceFilter2 = pTraceFilter2;
  469. }
  470. bool CTraceFilterChain::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  471. {
  472. bool bResult1 = true;
  473. bool bResult2 = true;
  474. if ( m_pTraceFilter1 )
  475. bResult1 = m_pTraceFilter1->ShouldHitEntity( pHandleEntity, contentsMask );
  476. if ( m_pTraceFilter2 )
  477. bResult2 = m_pTraceFilter2->ShouldHitEntity( pHandleEntity, contentsMask );
  478. return ( bResult1 && bResult2 );
  479. }
  480. //-----------------------------------------------------------------------------
  481. // Sweeps against a particular model, using collision rules
  482. //-----------------------------------------------------------------------------
  483. void UTIL_TraceModel( const Vector &vecStart, const Vector &vecEnd, const Vector &hullMin,
  484. const Vector &hullMax, CBaseEntity *pentModel, int collisionGroup, trace_t *ptr )
  485. {
  486. // Cull it....
  487. if ( pentModel && pentModel->ShouldCollide( collisionGroup, MASK_ALL ) )
  488. {
  489. Ray_t ray;
  490. ray.Init( vecStart, vecEnd, hullMin, hullMax );
  491. enginetrace->ClipRayToEntity( ray, MASK_ALL, pentModel, ptr );
  492. }
  493. else
  494. {
  495. memset( ptr, 0, sizeof(trace_t) );
  496. ptr->fraction = 1.0f;
  497. }
  498. }
  499. bool UTIL_EntityHasMatchingRootParent( CBaseEntity *pRootParent, CBaseEntity *pEntity )
  500. {
  501. if ( pRootParent )
  502. {
  503. // NOTE: Don't let siblings/parents collide.
  504. if ( pRootParent == pEntity->GetRootMoveParent() )
  505. return true;
  506. if ( pEntity->GetOwnerEntity() && pRootParent == pEntity->GetOwnerEntity()->GetRootMoveParent() )
  507. return true;
  508. }
  509. return false;
  510. }
  511. //-----------------------------------------------------------------------------
  512. // Sweep an entity from the starting to the ending position
  513. //-----------------------------------------------------------------------------
  514. class CTraceFilterEntity : public CTraceFilterSimple
  515. {
  516. DECLARE_CLASS( CTraceFilterEntity, CTraceFilterSimple );
  517. public:
  518. CTraceFilterEntity( CBaseEntity *pEntity, int nCollisionGroup )
  519. : CTraceFilterSimple( pEntity, nCollisionGroup )
  520. {
  521. m_pRootParent = pEntity->GetRootMoveParent();
  522. m_pEntity = pEntity;
  523. m_checkHash = g_EntityCollisionHash->IsObjectInHash(pEntity);
  524. }
  525. bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  526. {
  527. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  528. if ( !pEntity )
  529. return false;
  530. // Check parents against each other
  531. // NOTE: Don't let siblings/parents collide.
  532. if ( UTIL_EntityHasMatchingRootParent( m_pRootParent, pEntity ) )
  533. return false;
  534. if ( m_checkHash )
  535. {
  536. if ( g_EntityCollisionHash->IsObjectPairInHash( m_pEntity, pEntity ) )
  537. return false;
  538. }
  539. #ifndef CLIENT_DLL
  540. if ( m_pEntity->IsNPC() )
  541. {
  542. if ( NPC_CheckBrushExclude( m_pEntity, pEntity ) )
  543. return false;
  544. }
  545. #endif
  546. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  547. }
  548. private:
  549. CBaseEntity *m_pRootParent;
  550. CBaseEntity *m_pEntity;
  551. bool m_checkHash;
  552. };
  553. class CTraceFilterEntityIgnoreOther : public CTraceFilterEntity
  554. {
  555. DECLARE_CLASS( CTraceFilterEntityIgnoreOther, CTraceFilterEntity );
  556. public:
  557. CTraceFilterEntityIgnoreOther( CBaseEntity *pEntity, const IHandleEntity *pIgnore, int nCollisionGroup ) :
  558. CTraceFilterEntity( pEntity, nCollisionGroup ), m_pIgnoreOther( pIgnore )
  559. {
  560. }
  561. bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  562. {
  563. if ( pHandleEntity == m_pIgnoreOther )
  564. return false;
  565. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  566. }
  567. private:
  568. const IHandleEntity *m_pIgnoreOther;
  569. };
  570. //-----------------------------------------------------------------------------
  571. // Sweeps a particular entity through the world
  572. //-----------------------------------------------------------------------------
  573. void UTIL_TraceEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd, unsigned int mask, trace_t *ptr )
  574. {
  575. ICollideable *pCollision = pEntity->GetCollideable();
  576. //Vector vec = pCollision->OBBMaxs();
  577. // Adding this assertion here so game code catches it, but really the assertion belongs in the engine
  578. // because one day, rotated collideables will work!
  579. Assert( pCollision->GetCollisionAngles() == vec3_angle );
  580. CTraceFilterEntity traceFilter( pEntity, pCollision->GetCollisionGroup() );
  581. #ifdef PORTAL
  582. UTIL_Portal_TraceEntity( pEntity, vecAbsStart, vecAbsEnd, mask, &traceFilter, ptr );
  583. #else
  584. enginetrace->SweepCollideable( pCollision, vecAbsStart, vecAbsEnd, pCollision->GetCollisionAngles(), mask, &traceFilter, ptr );
  585. #endif
  586. }
  587. void UTIL_TraceEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd,
  588. unsigned int mask, const IHandleEntity *pIgnore, int nCollisionGroup, trace_t *ptr )
  589. {
  590. ICollideable *pCollision;
  591. pCollision = pEntity->GetCollideable();
  592. // Adding this assertion here so game code catches it, but really the assertion belongs in the engine
  593. // because one day, rotated collideables will work!
  594. Assert( pCollision->GetCollisionAngles() == vec3_angle );
  595. CTraceFilterEntityIgnoreOther traceFilter( pEntity, pIgnore, nCollisionGroup );
  596. #ifdef PORTAL
  597. UTIL_Portal_TraceEntity( pEntity, vecAbsStart, vecAbsEnd, mask, &traceFilter, ptr );
  598. #else
  599. enginetrace->SweepCollideable( pCollision, vecAbsStart, vecAbsEnd, pCollision->GetCollisionAngles(), mask, &traceFilter, ptr );
  600. #endif
  601. }
  602. void UTIL_TraceEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd,
  603. unsigned int mask, ITraceFilter *pFilter, trace_t *ptr )
  604. {
  605. ICollideable *pCollision;
  606. pCollision = pEntity->GetCollideable();
  607. // Adding this assertion here so game code catches it, but really the assertion belongs in the engine
  608. // because one day, rotated collideables will work!
  609. Assert( pCollision->GetCollisionAngles() == vec3_angle );
  610. #ifdef PORTAL
  611. UTIL_Portal_TraceEntity( pEntity, vecAbsStart, vecAbsEnd, mask, pFilter, ptr );
  612. #else
  613. enginetrace->SweepCollideable( pCollision, vecAbsStart, vecAbsEnd, pCollision->GetCollisionAngles(), mask, pFilter, ptr );
  614. #endif
  615. }
  616. // ----
  617. // This is basically a regular TraceLine that uses the FilterEntity filter.
  618. void UTIL_TraceLineFilterEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd,
  619. unsigned int mask, int nCollisionGroup, trace_t *ptr )
  620. {
  621. CTraceFilterEntity traceFilter( pEntity, nCollisionGroup );
  622. UTIL_TraceLine( vecAbsStart, vecAbsEnd, mask, &traceFilter, ptr );
  623. }
  624. void UTIL_ClipTraceToPlayers( const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask, ITraceFilter *filter, trace_t *tr )
  625. {
  626. trace_t playerTrace;
  627. Ray_t ray;
  628. float smallestFraction = tr->fraction;
  629. const float maxRange = 60.0f;
  630. ray.Init( vecAbsStart, vecAbsEnd );
  631. for ( int k = 1; k <= gpGlobals->maxClients; ++k )
  632. {
  633. CBasePlayer *player = UTIL_PlayerByIndex( k );
  634. if ( !player || !player->IsAlive() )
  635. continue;
  636. #ifdef CLIENT_DLL
  637. if ( player->IsDormant() )
  638. continue;
  639. #endif // CLIENT_DLL
  640. if ( filter && filter->ShouldHitEntity( player, mask ) == false )
  641. continue;
  642. float range = DistanceToRay( player->WorldSpaceCenter(), vecAbsStart, vecAbsEnd );
  643. if ( range < 0.0f || range > maxRange )
  644. continue;
  645. enginetrace->ClipRayToEntity( ray, mask|CONTENTS_HITBOX, player, &playerTrace );
  646. if ( playerTrace.fraction < smallestFraction )
  647. {
  648. // we shortened the ray - save off the trace
  649. *tr = playerTrace;
  650. smallestFraction = playerTrace.fraction;
  651. }
  652. }
  653. }
  654. //-----------------------------------------------------------------------------
  655. // Purpose: Make a tracer using a particle effect
  656. //-----------------------------------------------------------------------------
  657. void UTIL_ParticleTracer( const char *pszTracerEffectName, const Vector &vecStart, const Vector &vecEnd,
  658. int iEntIndex, int iAttachment, bool bWhiz )
  659. {
  660. int iParticleIndex = GetParticleSystemIndex( pszTracerEffectName );
  661. UTIL_Tracer( vecStart, vecEnd, iEntIndex, iAttachment, 0, bWhiz, "ParticleTracer", iParticleIndex );
  662. }
  663. //-----------------------------------------------------------------------------
  664. // Purpose: Make a tracer effect using the old, non-particle system, tracer effects.
  665. //-----------------------------------------------------------------------------
  666. void UTIL_Tracer( const Vector &vecStart, const Vector &vecEnd, int iEntIndex,
  667. int iAttachment, float flVelocity, bool bWhiz, const char *pCustomTracerName, int iParticleID )
  668. {
  669. CEffectData data;
  670. data.m_vStart = vecStart;
  671. data.m_vOrigin = vecEnd;
  672. #ifdef CLIENT_DLL
  673. data.m_hEntity = ClientEntityList().EntIndexToHandle( iEntIndex );
  674. #else
  675. data.m_nEntIndex = iEntIndex;
  676. #endif
  677. data.m_flScale = flVelocity;
  678. data.m_nHitBox = iParticleID;
  679. // Flags
  680. if ( bWhiz )
  681. {
  682. data.m_fFlags |= TRACER_FLAG_WHIZ;
  683. }
  684. if ( iAttachment != TRACER_DONT_USE_ATTACHMENT )
  685. {
  686. data.m_fFlags |= TRACER_FLAG_USEATTACHMENT;
  687. // Stomp the start, since it's not going to be used anyway
  688. data.m_nAttachmentIndex = iAttachment;
  689. }
  690. // Fire it off
  691. if ( pCustomTracerName )
  692. {
  693. DispatchEffect( pCustomTracerName, data );
  694. }
  695. else
  696. {
  697. DispatchEffect( "Tracer", data );
  698. }
  699. }
  700. void UTIL_BloodDrips( const Vector &origin, const Vector &direction, int color, int amount )
  701. {
  702. if ( !UTIL_ShouldShowBlood( color ) )
  703. return;
  704. if ( color == DONT_BLEED || amount == 0 )
  705. return;
  706. if ( g_Language.GetInt() == LANGUAGE_GERMAN && color == BLOOD_COLOR_RED )
  707. color = 0;
  708. if ( g_pGameRules->IsMultiplayer() )
  709. {
  710. // scale up blood effect in multiplayer for better visibility
  711. amount *= 5;
  712. }
  713. if ( amount > 255 )
  714. amount = 255;
  715. if (color == BLOOD_COLOR_MECH)
  716. {
  717. g_pEffects->Sparks(origin);
  718. if (random->RandomFloat(0, 2) >= 1)
  719. {
  720. UTIL_Smoke(origin, random->RandomInt(10, 15), 10);
  721. }
  722. }
  723. else
  724. {
  725. // Normal blood impact
  726. UTIL_BloodImpact( origin, direction, color, amount );
  727. }
  728. }
  729. //-----------------------------------------------------------------------------
  730. // Purpose: Returns low violence settings
  731. //-----------------------------------------------------------------------------
  732. static ConVar violence_hblood( "violence_hblood","1", 0, "Draw human blood" );
  733. static ConVar violence_hgibs( "violence_hgibs","1", 0, "Show human gib entities" );
  734. static ConVar violence_ablood( "violence_ablood","1", 0, "Draw alien blood" );
  735. static ConVar violence_agibs( "violence_agibs","1", 0, "Show alien gib entities" );
  736. bool UTIL_IsLowViolence( void )
  737. {
  738. // These convars are no longer necessary -- the engine is the final arbiter of
  739. // violence settings -- but they're here for legacy support and for testing low
  740. // violence when the engine is in normal violence mode.
  741. if ( !violence_hblood.GetBool() || !violence_ablood.GetBool() || !violence_hgibs.GetBool() || !violence_agibs.GetBool() )
  742. return true;
  743. return engine->IsLowViolence();
  744. }
  745. bool UTIL_ShouldShowBlood( int color )
  746. {
  747. if ( color != DONT_BLEED )
  748. {
  749. if ( color == BLOOD_COLOR_RED )
  750. {
  751. return violence_hblood.GetBool();
  752. }
  753. else
  754. {
  755. return violence_ablood.GetBool();
  756. }
  757. }
  758. return false;
  759. }
  760. //------------------------------------------------------------------------------
  761. // Purpose : Use trace to pass a specific decal type to the entity being decaled
  762. // Input :
  763. // Output :
  764. //------------------------------------------------------------------------------
  765. void UTIL_DecalTrace( trace_t *pTrace, char const *decalName )
  766. {
  767. if (pTrace->fraction == 1.0)
  768. return;
  769. CBaseEntity *pEntity = pTrace->m_pEnt;
  770. if ( !pEntity )
  771. return;
  772. pEntity->DecalTrace( pTrace, decalName );
  773. }
  774. void UTIL_BloodDecalTrace( trace_t *pTrace, int bloodColor )
  775. {
  776. if ( UTIL_ShouldShowBlood( bloodColor ) )
  777. {
  778. if ( bloodColor == BLOOD_COLOR_RED )
  779. {
  780. UTIL_DecalTrace( pTrace, "Blood" );
  781. }
  782. #if defined( HL2_EPISODIC )
  783. else if ( bloodColor == BLOOD_COLOR_BLOB )
  784. {
  785. UTIL_DecalTrace( pTrace, "BlobBlood" );
  786. }
  787. //don't draw a any decals if the blob is frozen
  788. else if ( bloodColor == BLOOD_COLOR_BLOB_FROZEN )
  789. {
  790. return;
  791. }
  792. #endif
  793. else if (bloodColor == BLOOD_COLOR_BRIGHTGREEN)
  794. {
  795. UTIL_DecalTrace( pTrace, "GreenBlood" );
  796. }
  797. else
  798. {
  799. UTIL_DecalTrace( pTrace, "YellowBlood" );
  800. }
  801. }
  802. }
  803. //-----------------------------------------------------------------------------
  804. // Purpose:
  805. // Input : &pos -
  806. // &dir -
  807. // color -
  808. // amount -
  809. //-----------------------------------------------------------------------------
  810. void UTIL_BloodImpact( const Vector &pos, const Vector &dir, int color, int amount )
  811. {
  812. CEffectData data;
  813. data.m_vOrigin = pos;
  814. data.m_vNormal = dir;
  815. data.m_flScale = (float)amount;
  816. data.m_nColor = (unsigned char)color;
  817. DispatchEffect( "bloodimpact", data );
  818. }
  819. bool UTIL_IsSpaceEmpty( CBaseEntity *pMainEnt, const Vector &vMin, const Vector &vMax )
  820. {
  821. Vector vHalfDims = ( vMax - vMin ) * 0.5f;
  822. Vector vCenter = vMin + vHalfDims;
  823. trace_t trace;
  824. int mask = (pMainEnt) ? pMainEnt->PhysicsSolidMaskForEntity() : MASK_SOLID;
  825. UTIL_TraceHull( vCenter, vCenter, -vHalfDims, vHalfDims, mask, pMainEnt, COLLISION_GROUP_NONE, &trace );
  826. bool bClear = ( trace.fraction == 1 && trace.allsolid != 1 && (trace.startsolid != 1) );
  827. return bClear;
  828. }
  829. bool UTIL_IsSpaceEmpty( CBaseEntity *pMainEnt, const Vector &vMin, const Vector &vMax, unsigned int mask, ITraceFilter *pFilter )
  830. {
  831. Vector vHalfDims = ( vMax - vMin ) * 0.5f;
  832. Vector vCenter = vMin + vHalfDims;
  833. trace_t trace;
  834. UTIL_TraceHull( vCenter, vCenter, -vHalfDims, vHalfDims, mask, pFilter, &trace );
  835. bool bClear = ( trace.fraction == 1 && trace.allsolid != 1 && (trace.startsolid != 1) );
  836. return bClear;
  837. }
  838. float UTIL_WaterLevel( const Vector &position, float minz, float maxz )
  839. {
  840. Vector midUp = position;
  841. midUp.z = minz;
  842. if ( !(UTIL_PointContents(midUp, MASK_WATER) & MASK_WATER) )
  843. return minz;
  844. midUp.z = maxz;
  845. if ( UTIL_PointContents(midUp, MASK_WATER) & MASK_WATER )
  846. return maxz;
  847. float diff = maxz - minz;
  848. while (diff > 1.0)
  849. {
  850. midUp.z = minz + diff/2.0;
  851. if ( UTIL_PointContents(midUp, MASK_WATER) & MASK_WATER )
  852. {
  853. minz = midUp.z;
  854. }
  855. else
  856. {
  857. maxz = midUp.z;
  858. }
  859. diff = maxz - minz;
  860. }
  861. return midUp.z;
  862. }
  863. //-----------------------------------------------------------------------------
  864. // Like UTIL_WaterLevel, but *way* less expensive.
  865. // I didn't replace UTIL_WaterLevel everywhere to avoid breaking anything.
  866. //-----------------------------------------------------------------------------
  867. class CWaterTraceFilter : public CTraceFilter
  868. {
  869. public:
  870. bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  871. {
  872. CBaseEntity *pCollide = EntityFromEntityHandle( pHandleEntity );
  873. // Static prop case...
  874. if ( !pCollide )
  875. return false;
  876. // Only impact water stuff...
  877. if ( pCollide->GetSolidFlags() & FSOLID_VOLUME_CONTENTS )
  878. return true;
  879. return false;
  880. }
  881. };
  882. float UTIL_FindWaterSurface( const Vector &position, float minz, float maxz )
  883. {
  884. Vector vecStart, vecEnd;
  885. vecStart.Init( position.x, position.y, maxz );
  886. vecEnd.Init( position.x, position.y, minz );
  887. Ray_t ray;
  888. trace_t tr;
  889. CWaterTraceFilter waterTraceFilter;
  890. ray.Init( vecStart, vecEnd );
  891. enginetrace->TraceRay( ray, MASK_WATER, &waterTraceFilter, &tr );
  892. return tr.endpos.z;
  893. }
  894. void UTIL_StringToFloatArray( float *pVector, int count, const char *pString )
  895. {
  896. char *pstr, *pfront, tempString[128];
  897. int j;
  898. Q_strncpy( tempString, pString, sizeof(tempString) );
  899. pstr = pfront = tempString;
  900. for ( j = 0; j < count; j++ ) // lifted from pr_edict.c
  901. {
  902. pVector[j] = atof( pfront );
  903. // skip any leading whitespace
  904. while ( *pstr && *pstr <= ' ' )
  905. pstr++;
  906. // skip to next whitespace
  907. while ( *pstr && *pstr > ' ' )
  908. pstr++;
  909. if (!*pstr)
  910. break;
  911. pstr++;
  912. pfront = pstr;
  913. }
  914. for ( j++; j < count; j++ )
  915. {
  916. pVector[j] = 0;
  917. }
  918. }
  919. void UTIL_StringToVector( float *pVector, const char *pString )
  920. {
  921. UTIL_StringToFloatArray( pVector, 3, pString );
  922. }
  923. void UTIL_DecodeICE( unsigned char * buffer, int size, const unsigned char *key )
  924. {
  925. if ( !key )
  926. return;
  927. IceKey ice( 0 ); // level 0 = 64bit key
  928. ice.set( key ); // set key
  929. int blockSize = ice.blockSize();
  930. unsigned char *temp = (unsigned char *) stackalloc( PAD_NUMBER( size, blockSize ) );
  931. unsigned char *p1 = buffer;
  932. unsigned char *p2 = temp;
  933. // encrypt data in 8 byte blocks
  934. int bytesLeft = size;
  935. while ( bytesLeft >= blockSize )
  936. {
  937. ice.decrypt( p1, p2 );
  938. bytesLeft -= blockSize;
  939. p1+=blockSize;
  940. p2+=blockSize;
  941. }
  942. // copy encrypted data back to original buffer
  943. Q_memcpy( buffer, temp, size-bytesLeft );
  944. }
  945. void UTIL_EncodeICE( unsigned char * buffer, unsigned int size, const unsigned char *key )
  946. {
  947. if ( !key )
  948. return;
  949. IceKey ice( 0 ); // level 0 = 64bit key
  950. ice.set( key ); // set key
  951. unsigned char *cipherText = buffer;
  952. unsigned char *plainText = buffer;
  953. uint bytesEncrypted = 0;
  954. while (bytesEncrypted < size)
  955. {
  956. ice.encrypt( plainText, cipherText );
  957. bytesEncrypted += 8;
  958. cipherText += 8;
  959. plainText += 8;
  960. }
  961. }
  962. // work-around since client header doesn't like inlined gpGlobals->curtime
  963. float IntervalTimer::Now( void ) const
  964. {
  965. return gpGlobals->curtime;
  966. }
  967. // work-around since client header doesn't like inlined gpGlobals->curtime
  968. float CountdownTimer::Now( void ) const
  969. {
  970. return gpGlobals->curtime;
  971. }
  972. BEGIN_DATADESC_NO_BASE( IntervalTimer )
  973. END_DATADESC()
  974. BEGIN_NETWORK_TABLE_NOBASE( IntervalTimer, DT_IntervalTimer )
  975. #ifdef CLIENT_DLL
  976. RecvPropFloat(RECVINFO(m_timestamp)),
  977. #else
  978. SendPropFloat (SENDINFO(m_timestamp), 0, SPROP_NOSCALE ),
  979. #endif
  980. END_NETWORK_TABLE()
  981. #ifdef CLIENT_DLL
  982. BEGIN_PREDICTION_DATA_NO_BASE( IntervalTimer )
  983. DEFINE_PRED_FIELD( m_timestamp, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  984. END_PREDICTION_DATA()
  985. #endif
  986. #ifdef CLIENT_DLL
  987. BEGIN_RECV_TABLE_NOBASE( CountdownTimer, DT_CountdownTimer )
  988. RecvPropFloat(RECVINFO(m_duration)),
  989. RecvPropFloat(RECVINFO(m_timestamp)),
  990. END_RECV_TABLE()
  991. BEGIN_PREDICTION_DATA_NO_BASE( CountdownTimer )
  992. DEFINE_PRED_FIELD( m_duration, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  993. DEFINE_PRED_FIELD( m_timestamp, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
  994. END_PREDICTION_DATA()
  995. #else
  996. BEGIN_SEND_TABLE_NOBASE( CountdownTimer, DT_CountdownTimer )
  997. SendPropFloat (SENDINFO(m_duration), 0, SPROP_NOSCALE ),
  998. SendPropFloat (SENDINFO(m_timestamp), 0, SPROP_NOSCALE ),
  999. END_SEND_TABLE()
  1000. #endif
  1001. BEGIN_DATADESC( CTimeline )
  1002. DEFINE_ARRAY( m_flValues, FIELD_FLOAT, TIMELINE_ARRAY_SIZE ),
  1003. DEFINE_ARRAY( m_nValueCounts, FIELD_FLOAT, TIMELINE_ARRAY_SIZE ),
  1004. DEFINE_FIELD( m_nBucketCount, FIELD_INTEGER ),
  1005. DEFINE_FIELD( m_flInterval, FIELD_FLOAT ),
  1006. DEFINE_FIELD( m_flFinalValue, FIELD_FLOAT ),
  1007. DEFINE_FIELD( m_nCompressionType, FIELD_INTEGER ),
  1008. DEFINE_FIELD( m_bStopped, FIELD_BOOLEAN ),
  1009. END_DATADESC()
  1010. BEGIN_NETWORK_TABLE_NOBASE( CTimeline, DT_Timeline )
  1011. #ifdef CLIENT_DLL
  1012. RecvPropArray3( RECVINFO_ARRAY( m_flValues ), RecvPropFloat( RECVINFO( m_flValues[0] ) ) ),
  1013. RecvPropArray3( RECVINFO_ARRAY( m_nValueCounts ), RecvPropFloat( RECVINFO( m_nValueCounts[0] ) ) ),
  1014. RecvPropInt( RECVINFO( m_nBucketCount ) ),
  1015. RecvPropFloat( RECVINFO( m_flInterval ) ),
  1016. RecvPropFloat( RECVINFO( m_flFinalValue ) ),
  1017. RecvPropInt( RECVINFO( m_nCompressionType ) ),
  1018. RecvPropBool( RECVINFO( m_bStopped ) ),
  1019. #else
  1020. SendPropArray3( SENDINFO_ARRAY3( m_flValues ), SendPropFloat( SENDINFO_ARRAY( m_flValues ), 0, SPROP_NOSCALE ) ),
  1021. SendPropArray3( SENDINFO_ARRAY3( m_nValueCounts ), SendPropFloat( SENDINFO_ARRAY( m_nValueCounts ), 0, SPROP_NOSCALE ) ),
  1022. SendPropInt( SENDINFO( m_nBucketCount ), NumBitsForCount( TIMELINE_ARRAY_SIZE ), SPROP_UNSIGNED ),
  1023. SendPropFloat( SENDINFO( m_flInterval ), 0, SPROP_NOSCALE ),
  1024. SendPropFloat( SENDINFO( m_flFinalValue ), 0, SPROP_NOSCALE ),
  1025. SendPropInt( SENDINFO( m_nCompressionType ), -1, SPROP_UNSIGNED ),
  1026. SendPropBool( SENDINFO( m_bStopped ) ),
  1027. #endif
  1028. END_NETWORK_TABLE()
  1029. void CTimeline::ClearValues( void )
  1030. {
  1031. Invalidate();
  1032. memset( m_flValues.m_Value, 0, sizeof( m_flValues.m_Value ) );
  1033. memset( m_nValueCounts.m_Value, 0, sizeof( m_nValueCounts.m_Value ) );
  1034. m_nBucketCount = 0;
  1035. m_flInterval = TIMELINE_INTERVAL_START;
  1036. m_flFinalValue = 0.0f;
  1037. m_bStopped = false;
  1038. }
  1039. void CTimeline::RecordValue( float flValue )
  1040. {
  1041. if ( !HasStarted() || m_bStopped )
  1042. return;
  1043. int iBucket = GetCurrentBucket();
  1044. Assert( iBucket >= 0 );
  1045. while ( iBucket >= TIMELINE_ARRAY_SIZE )
  1046. {
  1047. Compress();
  1048. iBucket = GetCurrentBucket();
  1049. }
  1050. if ( iBucket >= m_nBucketCount )
  1051. {
  1052. m_nBucketCount = iBucket + 1;
  1053. }
  1054. m_flValues.GetForModify( iBucket ) += flValue;
  1055. m_nValueCounts.GetForModify( iBucket )++;
  1056. if ( m_nCompressionType == TIMELINE_COMPRESSION_SUM ||
  1057. m_nCompressionType == TIMELINE_COMPRESSION_AVERAGE ||
  1058. m_nCompressionType == TIMELINE_COMPRESSION_AVERAGE_BLEND )
  1059. {
  1060. // Fill in blank preceding entries
  1061. float flCurrentValue = m_flValues[ iBucket ] / m_nValueCounts[ iBucket ];
  1062. int iPrecedingBucket = iBucket - 1;
  1063. while ( iPrecedingBucket >= 0 && m_nValueCounts[ iPrecedingBucket ] <= 0 )
  1064. {
  1065. iPrecedingBucket--;
  1066. }
  1067. // Get the last logged value (or the current if this is the first)
  1068. float flPrecedingValue = flCurrentValue;
  1069. if ( iPrecedingBucket >= 0 )
  1070. {
  1071. // Last logged value
  1072. if ( m_nCompressionType == TIMELINE_COMPRESSION_SUM )
  1073. {
  1074. flPrecedingValue = m_flValues[ iPrecedingBucket ];
  1075. if ( m_nValueCounts[ iBucket ] == 1 )
  1076. {
  1077. // Sum in the previous bucket if this is the first value in this bucket
  1078. m_flValues.GetForModify( iBucket ) += flPrecedingValue;
  1079. }
  1080. }
  1081. else
  1082. {
  1083. flPrecedingValue = m_flFinalValue;
  1084. }
  1085. }
  1086. // Number of buckets for blending from old to new value
  1087. float flNumBuckets = ( iBucket - iPrecedingBucket ) + 1;
  1088. if ( flNumBuckets >= 3.0f )
  1089. {
  1090. if ( m_nCompressionType == TIMELINE_COMPRESSION_AVERAGE_BLEND )
  1091. {
  1092. // Blend empty values between preceding and current
  1093. float flInterpBucket = 1.0f;
  1094. for ( int i = iPrecedingBucket + 1; i < iBucket; ++i, flInterpBucket += 1.0f )
  1095. {
  1096. float flInterp = flInterpBucket / flNumBuckets;
  1097. m_flValues.Set( i, flPrecedingValue * ( 1.0f - flInterp ) + flCurrentValue * flInterp );
  1098. m_nValueCounts.Set( i, 1 );
  1099. }
  1100. }
  1101. else
  1102. {
  1103. // Set empty values to preceding value
  1104. for ( int i = iPrecedingBucket + 1; i < iBucket; ++i )
  1105. {
  1106. m_flValues.Set( i, flPrecedingValue );
  1107. m_nValueCounts.Set( i, 1 );
  1108. }
  1109. }
  1110. }
  1111. }
  1112. m_flFinalValue = flValue;
  1113. }
  1114. float CTimeline::GetValue( int i ) const
  1115. {
  1116. Assert( i >= 0 && i < m_nBucketCount );
  1117. if ( i < 0 || i >= m_nBucketCount )
  1118. {
  1119. return 0.0f;
  1120. }
  1121. if ( m_nValueCounts[ i ] <= 0 )
  1122. {
  1123. return 0.0f;
  1124. }
  1125. else if ( i == 0 && m_nCompressionType == TIMELINE_COMPRESSION_SUM && m_nBucketCount > 1 )
  1126. {
  1127. // Aways start at 0 for sums!
  1128. return 0.0;
  1129. }
  1130. else
  1131. {
  1132. switch ( m_nCompressionType )
  1133. {
  1134. case TIMELINE_COMPRESSION_AVERAGE:
  1135. case TIMELINE_COMPRESSION_AVERAGE_BLEND:
  1136. return m_flValues[ i ] / m_nValueCounts[ i ];
  1137. case TIMELINE_COMPRESSION_SUM:
  1138. case TIMELINE_COMPRESSION_COUNT_PER_INTERVAL:
  1139. default:
  1140. return m_flValues[ i ];
  1141. }
  1142. }
  1143. }
  1144. float CTimeline::GetValueAtInterp( float fInterp ) const
  1145. {
  1146. if ( fInterp <= 0.0f )
  1147. {
  1148. return GetValue( 0 );
  1149. }
  1150. if ( fInterp >= 1.0f )
  1151. {
  1152. if ( m_nCompressionType == TIMELINE_COMPRESSION_SUM ||
  1153. m_nCompressionType == TIMELINE_COMPRESSION_COUNT_PER_INTERVAL )
  1154. {
  1155. return GetValue( Count() - 1 );
  1156. }
  1157. else
  1158. {
  1159. return m_flFinalValue;
  1160. }
  1161. }
  1162. float fBucket = fInterp * ( Count() - 1 );
  1163. int nBucket = fBucket;
  1164. fBucket -= nBucket;
  1165. float fValue = GetValue( nBucket );
  1166. float fNextValue = GetValue( nBucket + 1 );
  1167. return fValue * ( 1.0f - fBucket ) + fNextValue * fBucket;
  1168. }
  1169. void CTimeline::Compress( void )
  1170. {
  1171. int i, j;
  1172. switch ( m_nCompressionType )
  1173. {
  1174. case TIMELINE_COMPRESSION_SUM:
  1175. for ( i = 0, j = 0; i < TIMELINE_ARRAY_SIZE; i += 2, ++j )
  1176. {
  1177. m_flValues.GetForModify( j ) = MAX( m_flValues[ i ], m_flValues[ i + 1 ] );
  1178. m_nValueCounts.GetForModify( j ) = m_nValueCounts[ i ] + m_nValueCounts[ i + 1 ];
  1179. }
  1180. break;
  1181. default:
  1182. for ( i = 0, j = 0; i < TIMELINE_ARRAY_SIZE; i += 2, ++j )
  1183. {
  1184. m_flValues.GetForModify( j ) = m_flValues[ i ] + m_flValues[ i + 1 ];
  1185. m_nValueCounts.GetForModify( j ) = m_nValueCounts[ i ] + m_nValueCounts[ i + 1 ];
  1186. }
  1187. break;
  1188. }
  1189. int nRemainingBytes = ( TIMELINE_ARRAY_SIZE - j ) * sizeof( m_flValues[0] );
  1190. memset( &( m_flValues.GetForModify( j ) ), 0, nRemainingBytes );
  1191. memset( &( m_nValueCounts.GetForModify( j ) ), 0, nRemainingBytes );
  1192. m_flInterval *= 2.0f;
  1193. m_nBucketCount = j;
  1194. }
  1195. #ifdef CLIENT_DLL
  1196. CBasePlayer *UTIL_PlayerByIndex( int entindex )
  1197. {
  1198. // Sanity check the index being passed in
  1199. if ( entindex < 1 || entindex > gpGlobals->maxClients )
  1200. return NULL;
  1201. return ToBasePlayer( ClientEntityList().GetEnt( entindex ) );
  1202. }
  1203. #endif
  1204. unsigned short UTIL_GetAchievementEventMask( void )
  1205. {
  1206. CRC32_t mapCRC;
  1207. CRC32_Init( &mapCRC );
  1208. char lowercase[ 256 ];
  1209. #ifdef CLIENT_DLL
  1210. Q_FileBase( engine->GetLevelName(), lowercase, sizeof( lowercase ) );
  1211. #else
  1212. Q_strncpy( lowercase, STRING( gpGlobals->mapname ), sizeof( lowercase ) );
  1213. #endif
  1214. Q_strlower( lowercase );
  1215. CRC32_ProcessBuffer( &mapCRC, lowercase, Q_strlen( lowercase ) );
  1216. CRC32_Final( &mapCRC );
  1217. return ( mapCRC & 0xFFFF );
  1218. }
  1219. char* ReadAndAllocStringValue( KeyValues *pSub, const char *pName, const char *pFilename )
  1220. {
  1221. const char *pValue = pSub->GetString( pName, NULL );
  1222. if ( !pValue )
  1223. {
  1224. if ( pFilename )
  1225. {
  1226. DevWarning( "Can't get key value '%s' from file '%s'.\n", pName, pFilename );
  1227. }
  1228. return "";
  1229. }
  1230. int len = Q_strlen( pValue ) + 1;
  1231. char *pAlloced = new char[ len ];
  1232. Assert( pAlloced );
  1233. Q_strncpy( pAlloced, pValue, len );
  1234. return pAlloced;
  1235. }
  1236. int UTIL_StringFieldToInt( const char *szValue, const char **pValueStrings, int iNumStrings )
  1237. {
  1238. if ( !szValue || !szValue[0] )
  1239. return -1;
  1240. for ( int i = 0; i < iNumStrings; i++ )
  1241. {
  1242. if ( FStrEq(szValue, pValueStrings[i]) )
  1243. return i;
  1244. }
  1245. Assert(0);
  1246. return -1;
  1247. }
  1248. static char s_NumBitsInNibble[ 16 ] =
  1249. {
  1250. 0, // 0000 = 0
  1251. 1, // 0001 = 1
  1252. 1, // 0010 = 2
  1253. 2, // 0011 = 3
  1254. 1, // 0100 = 4
  1255. 2, // 0101 = 5
  1256. 2, // 0110 = 6
  1257. 3, // 0111 = 7
  1258. 1, // 1000 = 8
  1259. 2, // 1001 = 9
  1260. 2, // 1010 = 10
  1261. 3, // 1011 = 11
  1262. 2, // 1100 = 12
  1263. 3, // 1101 = 13
  1264. 3, // 1110 = 14
  1265. 4, // 1111 = 15
  1266. };
  1267. int UTIL_CountNumBitsSet( unsigned int nVar )
  1268. {
  1269. int nNumBits = 0;
  1270. while ( nVar > 0 )
  1271. {
  1272. // Look up and add in bits in the bottom nibble
  1273. nNumBits += s_NumBitsInNibble[ nVar & 0x0f ];
  1274. // Shift one nibble to the right
  1275. nVar >>= 4;
  1276. }
  1277. return nNumBits;
  1278. }
  1279. int UTIL_CountNumBitsSet( uint64 nVar )
  1280. {
  1281. int nNumBits = 0;
  1282. while ( nVar > 0 )
  1283. {
  1284. // Look up and add in bits in the bottom nibble
  1285. nNumBits += s_NumBitsInNibble[ nVar & 0x0f ];
  1286. // Shift one nibble to the right
  1287. nVar >>= 4;
  1288. }
  1289. return nNumBits;
  1290. }
  1291. bool UTIL_FindClosestPassableSpace( const Vector &vOriginalCenter, const Vector &vExtents, const Vector &vIndecisivePush, unsigned int iIterations, Vector &vCenterOut, int nAxisRestrictionFlags, FindClosestPassableSpace_TraceAdapter_t *pTraceAdapter )
  1292. {
  1293. Assert( vExtents != vec3_origin );
  1294. trace_t traces[2];
  1295. Ray_t entRay;
  1296. entRay.m_Extents = vExtents;
  1297. entRay.m_IsRay = false;
  1298. entRay.m_IsSwept = true;
  1299. entRay.m_StartOffset = vec3_origin;
  1300. Vector vOriginalExtents = vExtents;
  1301. Vector vCenter = vOriginalCenter;
  1302. Vector vGrowSize = vExtents * (1.0f / (float)(iIterations + 1));
  1303. Vector vCurrentExtents = vExtents - vGrowSize;
  1304. int iLargestExtent = 0;
  1305. {
  1306. float fLargestExtent = vOriginalExtents[0];
  1307. for( int i = 1; i != 3; ++i )
  1308. {
  1309. if( vOriginalExtents[i] > fLargestExtent )
  1310. {
  1311. iLargestExtent = i;
  1312. fLargestExtent = vOriginalExtents[i];
  1313. }
  1314. }
  1315. }
  1316. Ray_t testRay;
  1317. testRay.m_Extents = vGrowSize;
  1318. testRay.m_IsRay = false;
  1319. testRay.m_IsSwept = true;
  1320. testRay.m_StartOffset = vec3_origin;
  1321. float fOriginalExtentDists[8]; //distance between extents
  1322. //generate distance lookup. We reference this by XOR'ing the indices of two extents to find the axis of difference
  1323. {
  1324. //Since the ratios of lengths never change, we're going to normalize these distances to a value so we can simply scale on each iteration
  1325. //We've picked the largest extent as the basis simply because it's nonzero
  1326. float fNormalizer = 1.0f / vOriginalExtents[iLargestExtent];
  1327. float fXDiff = vOriginalExtents.x * 2.0f * fNormalizer;
  1328. float fXSqr = fXDiff * fXDiff;
  1329. float fYDiff = vOriginalExtents.y * 2.0f * fNormalizer;
  1330. float fYSqr = fYDiff * fYDiff;
  1331. float fZDiff = vOriginalExtents.z * 2.0f * fNormalizer;
  1332. float fZSqr = fZDiff * fZDiff;
  1333. fOriginalExtentDists[0] = 0.0f; //should never get hit
  1334. fOriginalExtentDists[1] = fXDiff; //line along x axis
  1335. fOriginalExtentDists[2] = fYDiff; //line along y axis
  1336. fOriginalExtentDists[3] = sqrt( fXSqr + fYSqr ); //diagonal perpendicular to z-axis
  1337. fOriginalExtentDists[4] = fZDiff; //line along z axis
  1338. fOriginalExtentDists[5] = sqrt( fXSqr + fZSqr ); //diagonal perpendicular to y-axis
  1339. fOriginalExtentDists[6] = sqrt( fYSqr + fZSqr ); //diagonal perpendicular to x-axis
  1340. fOriginalExtentDists[7] = sqrt( fXSqr + fYSqr + fZSqr ); //diagonal on all axes
  1341. }
  1342. Vector ptExtents[8]; //ordering is going to be like 3 bits, where 0 is a min on the related axis, and 1 is a max on the same axis, axis order x y z
  1343. float fExtentsValidation[8]; //some points are more valid than others, and this is our measure
  1344. vCenter.z += 0.001f; //to satisfy m_IsSwept on first pass
  1345. unsigned int iFailCount;
  1346. for( iFailCount = 0; iFailCount != iIterations; ++iFailCount )
  1347. {
  1348. //float fXDistribution[2] = { -vCurrentExtents.x, vCurrentExtents.x };
  1349. //float fYDistribution[3] = { -vCurrentExtents.y, 0.0f, vCurrentExtents.y };
  1350. //float fZDistribution[5] = { -vCurrentExtents.z, 0.0f, 0.0f, 0.0f, vCurrentExtents.z };
  1351. //hey look, they can overlap
  1352. float fExtentDistribution[6];
  1353. fExtentDistribution[ 0 ] = vCenter.z + ( ( ( nAxisRestrictionFlags & FL_AXIS_DIRECTION_NZ ) == 0 ) ? ( -vCurrentExtents.z ) : ( 0.0f ) ); // Z-
  1354. fExtentDistribution[ 1 ] = vCenter.x + ( ( ( nAxisRestrictionFlags & FL_AXIS_DIRECTION_NX ) == 0 ) ? ( -vCurrentExtents.x ) : ( 0.0f ) ); // X-
  1355. fExtentDistribution[ 2 ] = vCenter.x + ( ( ( nAxisRestrictionFlags & FL_AXIS_DIRECTION_X ) == 0 ) ? ( vCurrentExtents.x ) : ( 0.0f ) ); // X+
  1356. fExtentDistribution[ 3 ] = vCenter.y + ( ( ( nAxisRestrictionFlags & FL_AXIS_DIRECTION_NY ) == 0 ) ? ( -vCurrentExtents.y ) : ( 0.0f ) ); // Y-
  1357. fExtentDistribution[ 4 ] = vCenter.z + ( ( ( nAxisRestrictionFlags & FL_AXIS_DIRECTION_Z ) == 0 ) ? ( vCurrentExtents.z ) : ( 0.0f ) ); // Z+
  1358. fExtentDistribution[ 5 ] = vCenter.y + ( ( ( nAxisRestrictionFlags & FL_AXIS_DIRECTION_Y ) == 0 ) ? ( vCurrentExtents.y ) : ( 0.0f ) ); // Y+
  1359. float *pXDistribution = &fExtentDistribution[1];
  1360. float *pYDistribution = &fExtentDistribution[3];
  1361. bool bExtentInvalid[8];
  1362. float fExtentDists[8];
  1363. bool bAnyInvalid = false;
  1364. for( int i = 0; i != 8; ++i )
  1365. {
  1366. ptExtents[i].x = pXDistribution[i & (1<<0)]; //fExtentDistribution[(0 or 1) + 1]
  1367. ptExtents[i].y = pYDistribution[i & (1<<1)]; //fExtentDistribution[(0 or 2) + 3]
  1368. ptExtents[i].z = fExtentDistribution[i & (1<<2)]; //fExtentDistribution[(0 or 4)]
  1369. fExtentsValidation[i] = 0.0f;
  1370. bExtentInvalid[i] = pTraceAdapter->pPointOutsideWorldFunc( ptExtents[i], pTraceAdapter );
  1371. bAnyInvalid |= bExtentInvalid[i];
  1372. fExtentDists[i] = fOriginalExtentDists[i] * vExtents[iLargestExtent];
  1373. }
  1374. //trace from all extents to all other extents and rate the validity
  1375. {
  1376. unsigned int counters[2]; //I know it's weird, get over it
  1377. for( counters[0] = 0; counters[0] != 7; ++counters[0] )
  1378. {
  1379. for( counters[1] = counters[0] + 1; counters[1] != 8; ++counters[1] )
  1380. {
  1381. for( int i = 0; i != 2; ++i )
  1382. {
  1383. if( bExtentInvalid[counters[i]] )
  1384. {
  1385. traces[i].startsolid = true;
  1386. traces[i].fraction = 0.0f;
  1387. }
  1388. else
  1389. {
  1390. testRay.m_Start = ptExtents[counters[i]];
  1391. testRay.m_Delta = ptExtents[counters[1-i]] - ptExtents[counters[i]];
  1392. pTraceAdapter->pTraceFunc( testRay, &traces[i], pTraceAdapter );
  1393. }
  1394. }
  1395. float fDistance = fExtentDists[counters[0] ^ counters[1]];
  1396. for( int i = 0; i != 2; ++i )
  1397. {
  1398. if( (traces[i].fraction == 1.0f) && (traces[1-i].fraction != 1.0f) )
  1399. {
  1400. //One sided collision >_<
  1401. traces[i].startsolid = true;
  1402. traces[i].fraction = 0.0f;
  1403. break;
  1404. }
  1405. }
  1406. for( int i = 0; i != 2; ++i )
  1407. {
  1408. if( traces[i].startsolid )
  1409. {
  1410. bExtentInvalid[counters[i]] = true;
  1411. bAnyInvalid = true;
  1412. }
  1413. else
  1414. {
  1415. fExtentsValidation[counters[i]] += traces[i].fraction * fDistance;
  1416. }
  1417. }
  1418. }
  1419. }
  1420. }
  1421. //optimally we should do this check before tracing extents. But one sided collision is a bitch
  1422. if( !bAnyInvalid )
  1423. {
  1424. //try to trace back to the starting position (if we start in valid, the endpoint will be closer to the original center)
  1425. entRay.m_Start = vCenter;
  1426. entRay.m_Delta = vOriginalCenter - vCenter;
  1427. pTraceAdapter->pTraceFunc( entRay, &traces[0], pTraceAdapter );
  1428. if( traces[0].startsolid == false )
  1429. {
  1430. //damned one sided collision
  1431. vCenterOut = traces[0].endpos;
  1432. return !pTraceAdapter->pPointOutsideWorldFunc( vCenterOut, pTraceAdapter );
  1433. }
  1434. }
  1435. //find the direction to move based on the extent validity
  1436. {
  1437. Vector vNewOriginDirection( 0.0f, 0.0f, 0.0f );
  1438. float fTotalValidation = 0.0f;
  1439. for( int i = 0; i != 8; ++i )
  1440. {
  1441. if( !bExtentInvalid[i] )
  1442. {
  1443. vNewOriginDirection += (ptExtents[i] - vCenter) * fExtentsValidation[i];
  1444. fTotalValidation += fExtentsValidation[i];
  1445. }
  1446. }
  1447. if( fTotalValidation != 0.0f )
  1448. {
  1449. vCenter += (vNewOriginDirection / fTotalValidation);
  1450. //increase sizing
  1451. testRay.m_Extents += vGrowSize; //increase the ray size
  1452. vCurrentExtents -= vGrowSize; //while reducing the overall test region size (so outermost ray extents are the same)
  1453. }
  1454. else
  1455. {
  1456. //no point was valid, apply the indecisive vector
  1457. vCenter += vIndecisivePush;
  1458. //reset sizing
  1459. testRay.m_Extents = vGrowSize;
  1460. vCurrentExtents = vOriginalExtents - vGrowSize;
  1461. }
  1462. }
  1463. }
  1464. //Warning( "FindClosestPassableSpace() failure.\n" );
  1465. // X360TBD: Hits in portal devtest
  1466. //AssertMsg( IsGameConsole() || iFailCount != iIterations, "FindClosestPassableSpace() failure." );
  1467. vCenterOut = vOriginalCenter;
  1468. return false;
  1469. }
  1470. static void EngineTraceFunc( const Ray_t &ray, trace_t *pResult, FindClosestPassableSpace_TraceAdapter_t *pTraceAdapter )
  1471. {
  1472. enginetrace->TraceRay( ray, pTraceAdapter->fMask, pTraceAdapter->pTraceFilter, pResult );
  1473. }
  1474. static bool EnginePointOutsideWorldFunc( const Vector &vTest, FindClosestPassableSpace_TraceAdapter_t *pTraceAdapter )
  1475. {
  1476. return enginetrace->PointOutsideWorld( vTest );
  1477. }
  1478. bool UTIL_FindClosestPassableSpace( const Vector &vOriginalCenter, const Vector &vExtents, const Vector &vIndecisivePush, ITraceFilter *pTraceFilter, unsigned int fMask, unsigned int iIterations, Vector &vCenterOut, int nAxisRestrictionFlags )
  1479. {
  1480. FindClosestPassableSpace_TraceAdapter_t adapter;
  1481. adapter.pTraceFunc = EngineTraceFunc;
  1482. adapter.pPointOutsideWorldFunc = EnginePointOutsideWorldFunc;
  1483. adapter.pTraceFilter = pTraceFilter;
  1484. adapter.fMask = fMask;
  1485. return UTIL_FindClosestPassableSpace( vOriginalCenter, vExtents, vIndecisivePush, iIterations, vCenterOut, nAxisRestrictionFlags, &adapter );
  1486. }
  1487. bool UTIL_FindClosestPassableSpace( CBaseEntity *pEntity, const Vector &vIndecisivePush, unsigned int fMask, unsigned int iIterations, Vector &vOriginOut, Vector *pStartingPosition, int nAxisRestrictionFlags ) //assumes the object is already in a mostly passable space
  1488. {
  1489. // Don't ever do this to entities with a move parent
  1490. if ( pEntity->GetMoveParent() )
  1491. {
  1492. vOriginOut = pEntity->GetAbsOrigin();
  1493. return false;
  1494. }
  1495. Vector vEntityMaxs;
  1496. Vector vEntityMins;
  1497. pEntity->CollisionProp()->WorldSpaceAABB( &vEntityMins, &vEntityMaxs );
  1498. Vector ptEntityCenter = ((vEntityMins + vEntityMaxs) / 2.0f);
  1499. //vEntityMins -= ptEntityCenter;
  1500. vEntityMaxs -= ptEntityCenter;
  1501. Vector vCenterToOrigin = pEntity->GetAbsOrigin() - ptEntityCenter;
  1502. if( pStartingPosition != NULL )
  1503. {
  1504. Vector vOriginOffset = (*pStartingPosition) - pEntity->GetAbsOrigin();
  1505. ptEntityCenter += vOriginOffset;
  1506. }
  1507. CTraceFilterSimple traceFilter( pEntity, pEntity->GetCollisionGroup() );
  1508. Vector vResult;
  1509. bool bSuccess = UTIL_FindClosestPassableSpace( ptEntityCenter, vEntityMaxs, vIndecisivePush, &traceFilter, fMask, iIterations, vResult );
  1510. vResult += vCenterToOrigin;
  1511. vOriginOut = vResult;
  1512. return bSuccess;
  1513. }
  1514. bool UTIL_FindClosestPassableSpace( CBaseEntity *pEntity, const Vector &vIndecisivePush, unsigned int fMask, Vector *pStartingPosition, int nAxisRestrictionFlags )
  1515. {
  1516. Vector vNewPos;
  1517. bool bWorked = UTIL_FindClosestPassableSpace( pEntity, vIndecisivePush, fMask, 100, vNewPos, pStartingPosition, nAxisRestrictionFlags );
  1518. if( bWorked )
  1519. {
  1520. #ifdef CLIENT_DLL
  1521. pEntity->SetAbsOrigin( vNewPos );
  1522. #else
  1523. pEntity->Teleport( &vNewPos, NULL, NULL );
  1524. #endif
  1525. }
  1526. return bWorked;
  1527. }
  1528. //-----------------------------------------------------------------------------
  1529. // Purpose: Retrieves the MOD directory for the active game (ie. "hl2")
  1530. //-----------------------------------------------------------------------------
  1531. bool UTIL_GetModDir( char *lpszTextOut, unsigned int nSize )
  1532. {
  1533. // Must pass in a buffer at least large enough to hold the desired string
  1534. const char *pGameDir = CommandLine()->ParmValue( "-game", "hl2" );
  1535. Assert( strlen(pGameDir) <= nSize );
  1536. if ( strlen(pGameDir) > nSize )
  1537. return false;
  1538. Q_strncpy( lpszTextOut, pGameDir, nSize );
  1539. if ( Q_strnchr( lpszTextOut, '/', nSize ) || Q_strnchr( lpszTextOut, '\\', nSize ) )
  1540. {
  1541. // Strip the last directory off (which will be our game dir)
  1542. Q_StripLastDir( lpszTextOut, nSize );
  1543. // Find the difference in string lengths and take that difference from the original string as the mod dir
  1544. int dirlen = Q_strlen( lpszTextOut );
  1545. Q_strncpy( lpszTextOut, pGameDir + dirlen, Q_strlen( pGameDir ) - dirlen + 1 );
  1546. }
  1547. return true;
  1548. }
  1549. //#define FRUSTUM_DEBUGGING //for dumping some clipping information in UTIL_CalcFrustumThroughConvexPolygon
  1550. int UTIL_CalcFrustumThroughConvexPolygon( const Vector *pPolyVertices, int iPolyVertCount, const Vector &vFrustumOrigin, const VPlane *pInputFrustumPlanes, int iInputFrustumPlanes, VPlane *pOutputFrustumPlanes, int iMaxOutputPlanes, int iPreserveCount )
  1551. {
  1552. Assert( iPreserveCount <= iMaxOutputPlanes );
  1553. Assert( iPreserveCount <= iInputFrustumPlanes );
  1554. if( iPolyVertCount < 3 )
  1555. return 0;
  1556. #if defined( FRUSTUM_DEBUGGING )
  1557. //put case-specific debug logic here and it will propogate
  1558. const bool bDebugThisCall = (iInputFrustumPlanes > 0);
  1559. const float fDisplayTime = 0.0f;
  1560. #endif
  1561. int iMaxComplexity = iMaxOutputPlanes - iPreserveCount;
  1562. Vector *pClippedVerts;
  1563. int iClippedVertCount;
  1564. if( iInputFrustumPlanes > 0 )
  1565. {
  1566. //clip the polygon by the input frustum
  1567. int iAllocSize = iPolyVertCount + iInputFrustumPlanes;
  1568. Vector *pWorkVerts[2];
  1569. pWorkVerts[0] = (Vector *)stackalloc( sizeof( Vector ) * iAllocSize * 2 ); //possible to add 1 point per cut, iPolyVertCount starting points, iInputFrustumPlaneCount cuts
  1570. pWorkVerts[1] = pWorkVerts[0] + iAllocSize;
  1571. //clip by first plane and put output into pInVerts
  1572. iClippedVertCount = ClipPolyToPlane( (Vector *)pPolyVertices, iPolyVertCount, pWorkVerts[0], pInputFrustumPlanes[0].m_Normal, pInputFrustumPlanes[0].m_Dist, 0.01f );
  1573. //clip by other planes and flipflop in and out pointers
  1574. for( int i = 1; i != iInputFrustumPlanes; ++i )
  1575. {
  1576. if( iClippedVertCount < 3 )
  1577. return 0; //nothing left in the frustum
  1578. iClippedVertCount = ClipPolyToPlane( pWorkVerts[(i & 1) ^ 1], iClippedVertCount, pWorkVerts[i & 1], pInputFrustumPlanes[i].m_Normal, pInputFrustumPlanes[i].m_Dist, 0.01f );
  1579. }
  1580. if( iClippedVertCount < 3 )
  1581. return false; //nothing left in the frustum
  1582. pClippedVerts = pWorkVerts[(iInputFrustumPlanes & 1) ^ 1];
  1583. }
  1584. else
  1585. {
  1586. //no input frustum
  1587. if( iPolyVertCount > iMaxComplexity )
  1588. {
  1589. //we'll need to reduce our output frustum, copy the input polygon
  1590. pClippedVerts = (Vector *)stackalloc( sizeof( Vector ) * iPolyVertCount );
  1591. memcpy( pClippedVerts, pPolyVertices, sizeof( Vector ) * iPolyVertCount );
  1592. }
  1593. else
  1594. {
  1595. //we won't need to simplify the polygon to reduce output planes, just point at the input polygon
  1596. pClippedVerts = (Vector *)pPolyVertices;
  1597. }
  1598. iClippedVertCount = iPolyVertCount;
  1599. }
  1600. #if defined( FRUSTUM_DEBUGGING ) //for visibility culling debugging
  1601. if( bDebugThisCall )
  1602. {
  1603. NDebugOverlay::Line( pClippedVerts[iClippedVertCount - 1], pClippedVerts[0], 255, 0, 0, true, fDisplayTime );
  1604. for( int j = 0; j != iClippedVertCount - 1; ++j )
  1605. {
  1606. NDebugOverlay::Line( pClippedVerts[j], pClippedVerts[j+1], 255, 0, 0, true, fDisplayTime );
  1607. }
  1608. }
  1609. #endif
  1610. Assert( iClippedVertCount <= (iPolyVertCount + iInputFrustumPlanes) );
  1611. if( iClippedVertCount > iMaxComplexity )
  1612. {
  1613. #if defined( FRUSTUM_DEBUGGING )
  1614. if( bDebugThisCall )
  1615. {
  1616. NDebugOverlay::Line( pClippedVerts[iClippedVertCount - 1], pClippedVerts[0], 0, 255, 0, false, fDisplayTime );
  1617. for( int j = 0; j != iClippedVertCount - 1; ++j )
  1618. {
  1619. NDebugOverlay::Line( pClippedVerts[j], pClippedVerts[j+1], 0, 255, 0, true, fDisplayTime );
  1620. }
  1621. }
  1622. #endif
  1623. float *fLineLengthSqr = (float *)stackalloc( sizeof( float ) * iClippedVertCount );
  1624. for( int i = 0; i != (iClippedVertCount - 1); ++i )
  1625. {
  1626. fLineLengthSqr[i] = (pClippedVerts[i + 1] - pClippedVerts[i]).LengthSqr();
  1627. }
  1628. fLineLengthSqr[(iClippedVertCount - 1)] = (pClippedVerts[0] - pClippedVerts[(iClippedVertCount - 1)]).LengthSqr(); //wrap around
  1629. #if defined( FRUSTUM_DEBUGGING ) //for visibility culling debugging
  1630. Vector vDebugBoxExtent;
  1631. vDebugBoxExtent.Init( 1.0f, 1.0f, 1.0f );
  1632. #endif
  1633. while( iClippedVertCount > iMaxComplexity ) //vert count == number of planes we need to bound the polygon
  1634. {
  1635. //we have too many verts to represent this accurately in the output frustum plane count
  1636. //so, we're going to eliminate the smallest sides one at a time and bridge the surrounding sides until we're down to iMaxComplexity
  1637. float fMinSide = fLineLengthSqr[0];
  1638. int iMinSideFirstPoint = 0;
  1639. int iOldVertCount = iClippedVertCount;
  1640. --iClippedVertCount; //we're going to decrement this sometime in this block, it makes math easier to do it now
  1641. for( int i = 1; i != iOldVertCount; ++i )
  1642. {
  1643. if( fLineLengthSqr[i] < fMinSide )
  1644. {
  1645. fMinSide = fLineLengthSqr[i];
  1646. iMinSideFirstPoint = i;
  1647. }
  1648. }
  1649. int i1, i2, i3, i4;
  1650. i1 = (iMinSideFirstPoint + iClippedVertCount)%(iOldVertCount); //-1 with a wrap
  1651. i2 = iMinSideFirstPoint;
  1652. i3 = (iMinSideFirstPoint + 1)%(iOldVertCount);
  1653. i4 = (iMinSideFirstPoint + 2)%(iOldVertCount);
  1654. Vector *p1, *p2, *p3, *p4;
  1655. p1 = &pClippedVerts[i1];
  1656. p2 = &pClippedVerts[i2];
  1657. p3 = &pClippedVerts[i3]; //this is the one we'll actually be dropping in the merge
  1658. p4 = &pClippedVerts[i4];
  1659. //now we know the two points that we have to merge to one, project and make a merged point from the surrounding lines
  1660. //if( fMinSide >= 0.1f ) //only worth doing the math if it's actually going to be accurate and make a difference
  1661. {
  1662. //http://mathworld.wolfram.com/Line-LineIntersection.html (20)
  1663. Vector vA = *p2 - *p1;
  1664. Vector vB = *p4 - *p3;
  1665. Vector vC = *p3 - *p1;
  1666. Vector vCxB = vC.Cross( vB );
  1667. Vector vAxB = vA.Cross( vB );
  1668. float fS = vCxB.Dot(vAxB)/vAxB.LengthSqr();
  1669. *p2 = *p1 + (vA * fS);
  1670. fLineLengthSqr[i1] = (*p2 - *p1).LengthSqr();
  1671. }
  1672. fLineLengthSqr[i2] = (*p4 - *p2).LengthSqr(); //must do this BEFORE possibly shifting points p4+ left
  1673. if( i3 < i4 ) //not the last point in the array
  1674. {
  1675. int iElementShift = (iOldVertCount - i4);
  1676. //eliminate p3, we merged p2+p3 and already stored the result in p2
  1677. memmove( p3, p4, sizeof( Vector ) * iElementShift );
  1678. memmove( &fLineLengthSqr[i3], &fLineLengthSqr[i4], sizeof( float ) * iElementShift );
  1679. }
  1680. }
  1681. #if defined(FRUSTUM_DEBUGGING) //for visibility culling debugging
  1682. if( bDebugThisCall )
  1683. {
  1684. NDebugOverlay::Line( pClippedVerts[iClippedVertCount - 1], pClippedVerts[0], 0, 0, 255, false, fDisplayTime );
  1685. for( int j = 0; j != iClippedVertCount - 1; ++j )
  1686. {
  1687. NDebugOverlay::Line( pClippedVerts[j], pClippedVerts[j+1], 0, 0, 255, true, fDisplayTime );
  1688. }
  1689. }
  1690. #endif
  1691. }
  1692. //generate planes defined by each line around the convex and the frustum origin
  1693. {
  1694. int iFlipNormalsXOR = 0; //this algorithm was written assuming polygon vertices would be in a clockwise order from the perspective of vFrustumOrigin, some logic needs to flip if the inverse is true
  1695. {
  1696. Vector vLine1 = pPolyVertices[1] - pPolyVertices[0];
  1697. Vector vLine2 = pPolyVertices[2] - pPolyVertices[1];
  1698. Vector vFrontFace = vLine2.Cross( vLine1 );
  1699. iFlipNormalsXOR = (vFrontFace.Dot( vFrustumOrigin - pPolyVertices[0] ) < 0.0f) ? 1 : 0; //this will assist in reversing the normal by flipping the cross product
  1700. }
  1701. Vector vTemp[2];
  1702. vTemp[0] = pClippedVerts[iClippedVertCount - 1] - vFrustumOrigin;
  1703. for( int i = 0; i != iClippedVertCount; ++i )
  1704. {
  1705. int iIndexing = i & 1; //we can carry over the line computation from one iteration to the next, flip which order we look at the temps with
  1706. vTemp[iIndexing ^ 1] = pClippedVerts[i] - vFrustumOrigin;
  1707. Vector vNormal = vTemp[iIndexing ^ iFlipNormalsXOR].Cross( vTemp[(iIndexing ^ iFlipNormalsXOR) ^ 1] ); //vLine1.Cross( vLine2 );
  1708. vNormal.NormalizeInPlace();
  1709. pOutputFrustumPlanes[i].Init( vNormal, vNormal.Dot( vFrustumOrigin ) );
  1710. }
  1711. }
  1712. //preserve input planes on request
  1713. if( iPreserveCount > 0 )
  1714. {
  1715. memcpy( &pOutputFrustumPlanes[iClippedVertCount], &pInputFrustumPlanes[iInputFrustumPlanes - iPreserveCount], sizeof( VPlane ) * iPreserveCount );
  1716. }
  1717. return (iClippedVertCount + iPreserveCount);
  1718. }
  1719. //-----------------------------------------------------------------------------
  1720. // class CFlaggedEntitiesEnum
  1721. //-----------------------------------------------------------------------------
  1722. CFlaggedEntitiesEnum::CFlaggedEntitiesEnum( CBaseEntity **pList, int listMax, int flagMask )
  1723. {
  1724. m_pList = pList;
  1725. m_listMax = listMax;
  1726. m_flagMask = flagMask;
  1727. m_count = 0;
  1728. }
  1729. bool CFlaggedEntitiesEnum::AddToList( CBaseEntity *pEntity )
  1730. {
  1731. if ( m_count >= m_listMax )
  1732. {
  1733. AssertMsgOnce( 0, "reached enumerated list limit. Increase limit, decrease radius, or make it so entity flags will work for you" );
  1734. return false;
  1735. }
  1736. m_pList[m_count] = pEntity;
  1737. m_count++;
  1738. return true;
  1739. }
  1740. IterationRetval_t CFlaggedEntitiesEnum::EnumElement( IHandleEntity *pHandleEntity )
  1741. {
  1742. #if defined( CLIENT_DLL )
  1743. IClientEntity *pClientEntity = cl_entitylist->GetClientEntityFromHandle( pHandleEntity->GetRefEHandle() );
  1744. C_BaseEntity *pEntity = pClientEntity ? pClientEntity->GetBaseEntity() : NULL;
  1745. #else
  1746. CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
  1747. #endif
  1748. if ( pEntity )
  1749. {
  1750. if ( m_flagMask && !(pEntity->GetFlags() & m_flagMask) ) // Does it meet the criteria?
  1751. return ITERATION_CONTINUE;
  1752. if ( !AddToList( pEntity ) )
  1753. return ITERATION_STOP;
  1754. }
  1755. return ITERATION_CONTINUE;
  1756. }
  1757. //-----------------------------------------------------------------------------
  1758. // class CHurtableEntitiesEnum
  1759. //-----------------------------------------------------------------------------
  1760. CHurtableEntitiesEnum::CHurtableEntitiesEnum( CBaseEntity **pList, int listMax )
  1761. {
  1762. m_pList = pList;
  1763. m_listMax = listMax;
  1764. m_count = 0;
  1765. }
  1766. bool CHurtableEntitiesEnum::AddToList( CBaseEntity *pEntity )
  1767. {
  1768. if ( m_count >= m_listMax )
  1769. {
  1770. AssertMsgOnce( 0, "reached enumerated list limit. Increase limit, decrease radius, or make it so entity flags will work for you" );
  1771. return false;
  1772. }
  1773. m_pList[m_count] = pEntity;
  1774. m_count++;
  1775. return true;
  1776. }
  1777. IterationRetval_t CHurtableEntitiesEnum::EnumElement( IHandleEntity *pHandleEntity )
  1778. {
  1779. #if defined( CLIENT_DLL )
  1780. IClientEntity *pClientEntity = cl_entitylist->GetClientEntityFromHandle( pHandleEntity->GetRefEHandle() );
  1781. C_BaseEntity *pEntity = pClientEntity ? pClientEntity->GetBaseEntity() : NULL;
  1782. #else
  1783. CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
  1784. #endif
  1785. if ( pEntity )
  1786. {
  1787. if ( ( pEntity->m_takedamage == DAMAGE_NO || pEntity->GetHealth() <= 0 ) && pEntity->GetMoveType() != MOVETYPE_VPHYSICS ) // Does it meet the criteria?
  1788. return ITERATION_CONTINUE;
  1789. if ( !AddToList( pEntity ) )
  1790. return ITERATION_STOP;
  1791. }
  1792. return ITERATION_CONTINUE;
  1793. }
  1794. int UTIL_EntitiesAlongRay( const Ray_t &ray, CFlaggedEntitiesEnum *pEnum )
  1795. {
  1796. #if defined( CLIENT_DLL )
  1797. ::partition->EnumerateElementsAlongRay( PARTITION_CLIENT_NON_STATIC_EDICTS, ray, false, pEnum );
  1798. #else
  1799. ::partition->EnumerateElementsAlongRay( PARTITION_ENGINE_NON_STATIC_EDICTS, ray, false, pEnum );
  1800. #endif
  1801. return pEnum->GetCount();
  1802. }
  1803. //-----------------------------------------------------------------------------
  1804. // Returns the length of the next command
  1805. //-----------------------------------------------------------------------------
  1806. void UTIL_GetNextCommandLength( const char *pText, int nMaxLen, int *pCommandLength, int *pNextCommandOffset )
  1807. {
  1808. int nCommandLength = 0;
  1809. int nNextCommandOffset;
  1810. bool bIsQuoted = false;
  1811. bool bIsCommented = false;
  1812. for ( nNextCommandOffset=0; nNextCommandOffset < nMaxLen; ++nNextCommandOffset, nCommandLength += bIsCommented ? 0 : 1 )
  1813. {
  1814. char c = pText[nNextCommandOffset];
  1815. if ( !bIsCommented )
  1816. {
  1817. if ( c == '"' )
  1818. {
  1819. bIsQuoted = !bIsQuoted;
  1820. continue;
  1821. }
  1822. // don't break if inside a C++ style comment
  1823. if ( !bIsQuoted && c == '/' )
  1824. {
  1825. bIsCommented = ( nNextCommandOffset < nMaxLen-1 ) && pText[nNextCommandOffset+1] == '/';
  1826. if ( bIsCommented )
  1827. {
  1828. ++nNextCommandOffset;
  1829. continue;
  1830. }
  1831. }
  1832. // don't break if inside a quoted string
  1833. if ( !bIsQuoted && c == ';' )
  1834. break;
  1835. }
  1836. // FIXME: This is legacy behavior; should we not break if a \n is inside a quoted string?
  1837. if ( c == '\n' )
  1838. break;
  1839. }
  1840. *pCommandLength = nCommandLength;
  1841. *pNextCommandOffset = nNextCommandOffset;
  1842. }
  1843. /**
  1844. * remove double spaces and empty bold/italic HTML tags from a string
  1845. */
  1846. void UTIL_TrimEmptyWhitespaceFromHTML( OUT_Z_BYTECAP( descWriterByteSize ) wchar_t* pszDescWriter, size_t descWriterByteSize, const wchar_t* pszDescReader )
  1847. {
  1848. size_t writerArraySize = descWriterByteSize / sizeof( wchar_t );
  1849. wchar_t* pszDescWriterEnd = pszDescWriter + writerArraySize - 1; // leave 1 character for '\0' terminator
  1850. while ( *pszDescReader != 0 && pszDescWriter < pszDescWriterEnd )
  1851. {
  1852. if ( !wcsncmp( pszDescReader, L" <b></b> ", 9 ) ||
  1853. !wcsncmp( pszDescReader, L" <i></i> ", 9 ) )
  1854. {
  1855. pszDescReader += 8;
  1856. }
  1857. else if ( !wcsncmp( pszDescReader, L" ", 2 ) )
  1858. {
  1859. pszDescReader += 1;
  1860. }
  1861. else
  1862. {
  1863. *pszDescWriter = *pszDescReader;
  1864. pszDescWriter++;
  1865. pszDescReader++;
  1866. }
  1867. }
  1868. // terminate buffer
  1869. *pszDescWriter = 0;
  1870. }
  1871. void UTIL_TrimEmptyWhitespaceFromHTML( OUT_Z_BYTECAP( descWriterByteSize ) char* pszDescWriter, size_t descWriterByteSize, const char* pszDescReader )
  1872. {
  1873. size_t writerArraySize = descWriterByteSize / sizeof( char );
  1874. char* pszDescWriterEnd = pszDescWriter + writerArraySize - 1; // leave 1 character for '\0' terminator
  1875. while ( *pszDescReader != 0 && pszDescWriter < pszDescWriterEnd )
  1876. {
  1877. if ( !strncmp( pszDescReader, " <b></b> ", 9 ) ||
  1878. !strncmp( pszDescReader, " <i></i> ", 9 ) )
  1879. {
  1880. pszDescReader += 8;
  1881. }
  1882. else if ( !strncmp( pszDescReader, " ", 2 ) )
  1883. {
  1884. pszDescReader += 1;
  1885. }
  1886. else
  1887. {
  1888. *pszDescWriter = *pszDescReader;
  1889. pszDescWriter++;
  1890. pszDescReader++;
  1891. }
  1892. }
  1893. // terminate buffer
  1894. *pszDescWriter = 0;
  1895. }