Team Fortress 2 Source Code as on 22/4/2020
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.

1406 lines
40 KiB

  1. //========= Copyright 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. #ifdef TF_CLIENT_DLL
  16. #include "cdll_util.h"
  17. #endif
  18. #include "particle_parse.h"
  19. #include "KeyValues.h"
  20. #include "time.h"
  21. #ifdef USES_ECON_ITEMS
  22. #include "econ_item_constants.h"
  23. #include "econ_holidays.h"
  24. #include "rtime.h"
  25. #endif // USES_ECON_ITEMS
  26. #ifdef CLIENT_DLL
  27. #include "c_te_effect_dispatch.h"
  28. #else
  29. #include "te_effect_dispatch.h"
  30. bool NPC_CheckBrushExclude( CBaseEntity *pEntity, CBaseEntity *pBrush );
  31. #endif
  32. #include "steam/steam_api.h"
  33. // memdbgon must be the last include file in a .cpp file!!!
  34. #include "tier0/memdbgon.h"
  35. ConVar r_visualizetraces( "r_visualizetraces", "0", FCVAR_CHEAT );
  36. ConVar developer("developer", "0", 0, "Set developer message level" ); // developer mode
  37. float UTIL_VecToYaw( const Vector &vec )
  38. {
  39. if (vec.y == 0 && vec.x == 0)
  40. return 0;
  41. float yaw = atan2( vec.y, vec.x );
  42. yaw = RAD2DEG(yaw);
  43. if (yaw < 0)
  44. yaw += 360;
  45. return yaw;
  46. }
  47. float UTIL_VecToPitch( const Vector &vec )
  48. {
  49. if (vec.y == 0 && vec.x == 0)
  50. {
  51. if (vec.z < 0)
  52. return 180.0;
  53. else
  54. return -180.0;
  55. }
  56. float dist = vec.Length2D();
  57. float pitch = atan2( -vec.z, dist );
  58. pitch = RAD2DEG(pitch);
  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. Vector tmp = vec;
  78. VectorNormalize( tmp );
  79. float x = matrix[0][0] * tmp.x + matrix[1][0] * tmp.y + matrix[2][0] * tmp.z;
  80. float z = matrix[0][2] * tmp.x + matrix[1][2] * tmp.y + matrix[2][2] * tmp.z;
  81. if (x == 0.0f && z == 0.0f)
  82. return 0.0f;
  83. float pitch = atan2( z, x );
  84. pitch = RAD2DEG(pitch);
  85. if (pitch < 0)
  86. pitch += 360;
  87. return pitch;
  88. }
  89. Vector UTIL_YawToVector( float yaw )
  90. {
  91. Vector ret;
  92. ret.z = 0;
  93. float angle = DEG2RAD( yaw );
  94. SinCos( angle, &ret.y, &ret.x );
  95. return ret;
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Purpose: Helper function get get determinisitc random values for shared/prediction code
  99. // Input : seedvalue -
  100. // *module -
  101. // line -
  102. // Output : static int
  103. //-----------------------------------------------------------------------------
  104. static int SeedFileLineHash( int seedvalue, const char *sharedname, int additionalSeed )
  105. {
  106. CRC32_t retval;
  107. CRC32_Init( &retval );
  108. CRC32_ProcessBuffer( &retval, (void *)&seedvalue, sizeof( int ) );
  109. CRC32_ProcessBuffer( &retval, (void *)&additionalSeed, sizeof( int ) );
  110. CRC32_ProcessBuffer( &retval, (void *)sharedname, Q_strlen( sharedname ) );
  111. CRC32_Final( &retval );
  112. return (int)( retval );
  113. }
  114. float SharedRandomFloat( const char *sharedname, float flMinVal, float flMaxVal, int additionalSeed /*=0*/ )
  115. {
  116. Assert( CBaseEntity::GetPredictionRandomSeed() != -1 );
  117. int seed = SeedFileLineHash( CBaseEntity::GetPredictionRandomSeed(), sharedname, additionalSeed );
  118. RandomSeed( seed );
  119. return RandomFloat( flMinVal, flMaxVal );
  120. }
  121. int SharedRandomInt( const char *sharedname, int iMinVal, int iMaxVal, int additionalSeed /*=0*/ )
  122. {
  123. Assert( CBaseEntity::GetPredictionRandomSeed() != -1 );
  124. int seed = SeedFileLineHash( CBaseEntity::GetPredictionRandomSeed(), sharedname, additionalSeed );
  125. RandomSeed( seed );
  126. return RandomInt( iMinVal, iMaxVal );
  127. }
  128. Vector SharedRandomVector( const char *sharedname, float minVal, float maxVal, int additionalSeed /*=0*/ )
  129. {
  130. Assert( CBaseEntity::GetPredictionRandomSeed() != -1 );
  131. int seed = SeedFileLineHash( CBaseEntity::GetPredictionRandomSeed(), sharedname, additionalSeed );
  132. RandomSeed( seed );
  133. // HACK: Can't call RandomVector/Angle because it uses rand() not vstlib Random*() functions!
  134. // Get a random vector.
  135. Vector vRandom;
  136. vRandom.x = RandomFloat( minVal, maxVal );
  137. vRandom.y = RandomFloat( minVal, maxVal );
  138. vRandom.z = RandomFloat( minVal, maxVal );
  139. return vRandom;
  140. }
  141. QAngle SharedRandomAngle( const char *sharedname, float minVal, float maxVal, int additionalSeed /*=0*/ )
  142. {
  143. Assert( CBaseEntity::GetPredictionRandomSeed() != -1 );
  144. int seed = SeedFileLineHash( CBaseEntity::GetPredictionRandomSeed(), sharedname, additionalSeed );
  145. RandomSeed( seed );
  146. // HACK: Can't call RandomVector/Angle because it uses rand() not vstlib Random*() functions!
  147. // Get a random vector.
  148. Vector vRandom;
  149. vRandom.x = RandomFloat( minVal, maxVal );
  150. vRandom.y = RandomFloat( minVal, maxVal );
  151. vRandom.z = RandomFloat( minVal, maxVal );
  152. return QAngle( vRandom.x, vRandom.y, vRandom.z );
  153. }
  154. //-----------------------------------------------------------------------------
  155. //
  156. // Shared client/server trace filter code
  157. //
  158. //-----------------------------------------------------------------------------
  159. bool PassServerEntityFilter( const IHandleEntity *pTouch, const IHandleEntity *pPass )
  160. {
  161. if ( !pPass )
  162. return true;
  163. if ( pTouch == pPass )
  164. return false;
  165. const CBaseEntity *pEntTouch = EntityFromEntityHandle( pTouch );
  166. const CBaseEntity *pEntPass = EntityFromEntityHandle( pPass );
  167. if ( !pEntTouch || !pEntPass )
  168. return true;
  169. // don't clip against own missiles
  170. if ( pEntTouch->GetOwnerEntity() == pEntPass )
  171. return false;
  172. // don't clip against owner
  173. if ( pEntPass->GetOwnerEntity() == pEntTouch )
  174. return false;
  175. return true;
  176. }
  177. //-----------------------------------------------------------------------------
  178. // A standard filter to be applied to just about everything.
  179. //-----------------------------------------------------------------------------
  180. bool StandardFilterRules( IHandleEntity *pHandleEntity, int fContentsMask )
  181. {
  182. CBaseEntity *pCollide = EntityFromEntityHandle( pHandleEntity );
  183. // Static prop case...
  184. if ( !pCollide )
  185. return true;
  186. SolidType_t solid = pCollide->GetSolid();
  187. const model_t *pModel = pCollide->GetModel();
  188. if ( ( modelinfo->GetModelType( pModel ) != mod_brush ) || (solid != SOLID_BSP && solid != SOLID_VPHYSICS) )
  189. {
  190. if ( (fContentsMask & CONTENTS_MONSTER) == 0 )
  191. return false;
  192. }
  193. // This code is used to cull out tests against see-thru entities
  194. if ( !(fContentsMask & CONTENTS_WINDOW) && pCollide->IsTransparent() )
  195. return false;
  196. // FIXME: this is to skip BSP models that are entities that can be
  197. // potentially moved/deleted, similar to a monster but doors don't seem to
  198. // be flagged as monsters
  199. // FIXME: the FL_WORLDBRUSH looked promising, but it needs to be set on
  200. // everything that's actually a worldbrush and it currently isn't
  201. if ( !(fContentsMask & CONTENTS_MOVEABLE) && (pCollide->GetMoveType() == MOVETYPE_PUSH))// !(touch->flags & FL_WORLDBRUSH) )
  202. return false;
  203. return true;
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Simple trace filter
  207. //-----------------------------------------------------------------------------
  208. CTraceFilterSimple::CTraceFilterSimple( const IHandleEntity *passedict, int collisionGroup,
  209. ShouldHitFunc_t pExtraShouldHitFunc )
  210. {
  211. m_pPassEnt = passedict;
  212. m_collisionGroup = collisionGroup;
  213. m_pExtraShouldHitCheckFunction = pExtraShouldHitFunc;
  214. }
  215. //-----------------------------------------------------------------------------
  216. // The trace filter!
  217. //-----------------------------------------------------------------------------
  218. bool CTraceFilterSimple::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  219. {
  220. if ( !StandardFilterRules( pHandleEntity, contentsMask ) )
  221. return false;
  222. if ( m_pPassEnt )
  223. {
  224. if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) )
  225. {
  226. return false;
  227. }
  228. }
  229. // Don't test if the game code tells us we should ignore this collision...
  230. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  231. if ( !pEntity )
  232. return false;
  233. if ( !pEntity->ShouldCollide( m_collisionGroup, contentsMask ) )
  234. return false;
  235. if ( pEntity && !g_pGameRules->ShouldCollide( m_collisionGroup, pEntity->GetCollisionGroup() ) )
  236. return false;
  237. if ( m_pExtraShouldHitCheckFunction &&
  238. (! ( m_pExtraShouldHitCheckFunction( pHandleEntity, contentsMask ) ) ) )
  239. return false;
  240. return true;
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Purpose: Trace filter that only hits NPCs and the player
  244. //-----------------------------------------------------------------------------
  245. bool CTraceFilterOnlyNPCsAndPlayer::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  246. {
  247. if ( CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask ) )
  248. {
  249. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  250. if ( !pEntity )
  251. return false;
  252. #ifdef CSTRIKE_DLL
  253. #ifndef CLIENT_DLL
  254. if ( pEntity->Classify() == CLASS_PLAYER_ALLY )
  255. return true; // CS hostages are CLASS_PLAYER_ALLY but not IsNPC()
  256. #endif // !CLIENT_DLL
  257. #endif // CSTRIKE_DLL
  258. return (pEntity->IsNPC() || pEntity->IsPlayer());
  259. }
  260. return false;
  261. }
  262. //-----------------------------------------------------------------------------
  263. // Purpose: Trace filter that only hits anything but NPCs and the player
  264. //-----------------------------------------------------------------------------
  265. bool CTraceFilterNoNPCsOrPlayer::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  266. {
  267. if ( CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask ) )
  268. {
  269. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  270. if ( !pEntity )
  271. return NULL;
  272. #ifndef CLIENT_DLL
  273. if ( pEntity->Classify() == CLASS_PLAYER_ALLY )
  274. return false; // CS hostages are CLASS_PLAYER_ALLY but not IsNPC()
  275. #endif
  276. return (!pEntity->IsNPC() && !pEntity->IsPlayer());
  277. }
  278. return false;
  279. }
  280. //-----------------------------------------------------------------------------
  281. // Trace filter that skips two entities
  282. //-----------------------------------------------------------------------------
  283. CTraceFilterSkipTwoEntities::CTraceFilterSkipTwoEntities( const IHandleEntity *passentity, const IHandleEntity *passentity2, int collisionGroup ) :
  284. BaseClass( passentity, collisionGroup ), m_pPassEnt2(passentity2)
  285. {
  286. }
  287. bool CTraceFilterSkipTwoEntities::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  288. {
  289. Assert( pHandleEntity );
  290. if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt2 ) )
  291. return false;
  292. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Trace filter that can take a list of entities to ignore
  296. //-----------------------------------------------------------------------------
  297. CTraceFilterSimpleList::CTraceFilterSimpleList( int collisionGroup ) :
  298. CTraceFilterSimple( NULL, collisionGroup )
  299. {
  300. }
  301. //-----------------------------------------------------------------------------
  302. // Purpose:
  303. //-----------------------------------------------------------------------------
  304. bool CTraceFilterSimpleList::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  305. {
  306. if ( m_PassEntities.Find(pHandleEntity) != m_PassEntities.InvalidIndex() )
  307. return false;
  308. return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
  309. }
  310. //-----------------------------------------------------------------------------
  311. // Purpose: Add an entity to my list of entities to ignore in the trace
  312. //-----------------------------------------------------------------------------
  313. void CTraceFilterSimpleList::AddEntityToIgnore( IHandleEntity *pEntity )
  314. {
  315. m_PassEntities.AddToTail( pEntity );
  316. }
  317. //-----------------------------------------------------------------------------
  318. // Purpose: Custom trace filter used for NPC LOS traces
  319. //-----------------------------------------------------------------------------
  320. CTraceFilterLOS::CTraceFilterLOS( IHandleEntity *pHandleEntity, int collisionGroup, IHandleEntity *pHandleEntity2 ) :
  321. CTraceFilterSkipTwoEntities( pHandleEntity, pHandleEntity2, collisionGroup )
  322. {
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose:
  326. //-----------------------------------------------------------------------------
  327. bool CTraceFilterLOS::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  328. {
  329. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  330. if ( !pEntity->BlocksLOS() )
  331. return false;
  332. return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Trace filter that can take a classname to ignore
  336. //-----------------------------------------------------------------------------
  337. CTraceFilterSkipClassname::CTraceFilterSkipClassname( const IHandleEntity *passentity, const char *pchClassname, int collisionGroup ) :
  338. CTraceFilterSimple( passentity, collisionGroup ), m_pchClassname( pchClassname )
  339. {
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Purpose:
  343. //-----------------------------------------------------------------------------
  344. bool CTraceFilterSkipClassname::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  345. {
  346. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  347. if ( !pEntity || FClassnameIs( pEntity, m_pchClassname ) )
  348. return false;
  349. return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
  350. }
  351. //-----------------------------------------------------------------------------
  352. // Trace filter that skips two classnames
  353. //-----------------------------------------------------------------------------
  354. CTraceFilterSkipTwoClassnames::CTraceFilterSkipTwoClassnames( const IHandleEntity *passentity, const char *pchClassname, const char *pchClassname2, int collisionGroup ) :
  355. BaseClass( passentity, pchClassname, collisionGroup ), m_pchClassname2(pchClassname2)
  356. {
  357. }
  358. bool CTraceFilterSkipTwoClassnames::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  359. {
  360. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  361. if ( !pEntity || FClassnameIs( pEntity, m_pchClassname2 ) )
  362. return false;
  363. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  364. }
  365. //-----------------------------------------------------------------------------
  366. // Trace filter that can take a list of entities to ignore
  367. //-----------------------------------------------------------------------------
  368. CTraceFilterSimpleClassnameList::CTraceFilterSimpleClassnameList( const IHandleEntity *passentity, int collisionGroup ) :
  369. CTraceFilterSimple( passentity, collisionGroup )
  370. {
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose:
  374. //-----------------------------------------------------------------------------
  375. bool CTraceFilterSimpleClassnameList::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  376. {
  377. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  378. if ( !pEntity )
  379. return false;
  380. for ( int i = 0; i < m_PassClassnames.Count(); ++i )
  381. {
  382. if ( FClassnameIs( pEntity, m_PassClassnames[ i ] ) )
  383. return false;
  384. }
  385. return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
  386. }
  387. //-----------------------------------------------------------------------------
  388. // Purpose: Add an entity to my list of entities to ignore in the trace
  389. //-----------------------------------------------------------------------------
  390. void CTraceFilterSimpleClassnameList::AddClassnameToIgnore( const char *pchClassname )
  391. {
  392. m_PassClassnames.AddToTail( pchClassname );
  393. }
  394. CTraceFilterChain::CTraceFilterChain( ITraceFilter *pTraceFilter1, ITraceFilter *pTraceFilter2 )
  395. {
  396. m_pTraceFilter1 = pTraceFilter1;
  397. m_pTraceFilter2 = pTraceFilter2;
  398. }
  399. bool CTraceFilterChain::ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  400. {
  401. bool bResult1 = true;
  402. bool bResult2 = true;
  403. if ( m_pTraceFilter1 )
  404. bResult1 = m_pTraceFilter1->ShouldHitEntity( pHandleEntity, contentsMask );
  405. if ( m_pTraceFilter2 )
  406. bResult2 = m_pTraceFilter2->ShouldHitEntity( pHandleEntity, contentsMask );
  407. return ( bResult1 && bResult2 );
  408. }
  409. //-----------------------------------------------------------------------------
  410. // Sweeps against a particular model, using collision rules
  411. //-----------------------------------------------------------------------------
  412. void UTIL_TraceModel( const Vector &vecStart, const Vector &vecEnd, const Vector &hullMin,
  413. const Vector &hullMax, CBaseEntity *pentModel, int collisionGroup, trace_t *ptr )
  414. {
  415. // Cull it....
  416. if ( pentModel && pentModel->ShouldCollide( collisionGroup, MASK_ALL ) )
  417. {
  418. Ray_t ray;
  419. ray.Init( vecStart, vecEnd, hullMin, hullMax );
  420. enginetrace->ClipRayToEntity( ray, MASK_ALL, pentModel, ptr );
  421. }
  422. else
  423. {
  424. memset( ptr, 0, sizeof(trace_t) );
  425. ptr->fraction = 1.0f;
  426. }
  427. }
  428. bool UTIL_EntityHasMatchingRootParent( CBaseEntity *pRootParent, CBaseEntity *pEntity )
  429. {
  430. if ( pRootParent )
  431. {
  432. // NOTE: Don't let siblings/parents collide.
  433. if ( pRootParent == pEntity->GetRootMoveParent() )
  434. return true;
  435. if ( pEntity->GetOwnerEntity() && pRootParent == pEntity->GetOwnerEntity()->GetRootMoveParent() )
  436. return true;
  437. }
  438. return false;
  439. }
  440. //-----------------------------------------------------------------------------
  441. // Sweep an entity from the starting to the ending position
  442. //-----------------------------------------------------------------------------
  443. class CTraceFilterEntity : public CTraceFilterSimple
  444. {
  445. DECLARE_CLASS( CTraceFilterEntity, CTraceFilterSimple );
  446. public:
  447. CTraceFilterEntity( CBaseEntity *pEntity, int nCollisionGroup )
  448. : CTraceFilterSimple( pEntity, nCollisionGroup )
  449. {
  450. m_pRootParent = pEntity->GetRootMoveParent();
  451. m_pEntity = pEntity;
  452. m_checkHash = g_EntityCollisionHash->IsObjectInHash(pEntity);
  453. }
  454. bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  455. {
  456. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  457. if ( !pEntity )
  458. return false;
  459. // Check parents against each other
  460. // NOTE: Don't let siblings/parents collide.
  461. if ( UTIL_EntityHasMatchingRootParent( m_pRootParent, pEntity ) )
  462. return false;
  463. if ( m_checkHash )
  464. {
  465. if ( g_EntityCollisionHash->IsObjectPairInHash( m_pEntity, pEntity ) )
  466. return false;
  467. }
  468. #ifndef CLIENT_DLL
  469. if ( m_pEntity->IsNPC() )
  470. {
  471. if ( NPC_CheckBrushExclude( m_pEntity, pEntity ) )
  472. return false;
  473. }
  474. #endif
  475. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  476. }
  477. private:
  478. CBaseEntity *m_pRootParent;
  479. CBaseEntity *m_pEntity;
  480. bool m_checkHash;
  481. };
  482. class CTraceFilterEntityIgnoreOther : public CTraceFilterEntity
  483. {
  484. DECLARE_CLASS( CTraceFilterEntityIgnoreOther, CTraceFilterEntity );
  485. public:
  486. CTraceFilterEntityIgnoreOther( CBaseEntity *pEntity, const IHandleEntity *pIgnore, int nCollisionGroup ) :
  487. CTraceFilterEntity( pEntity, nCollisionGroup ), m_pIgnoreOther( pIgnore )
  488. {
  489. }
  490. bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  491. {
  492. if ( pHandleEntity == m_pIgnoreOther )
  493. return false;
  494. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  495. }
  496. private:
  497. const IHandleEntity *m_pIgnoreOther;
  498. };
  499. //-----------------------------------------------------------------------------
  500. // Sweeps a particular entity through the world
  501. //-----------------------------------------------------------------------------
  502. void UTIL_TraceEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd, unsigned int mask, trace_t *ptr )
  503. {
  504. ICollideable *pCollision = pEntity->GetCollideable();
  505. // Adding this assertion here so game code catches it, but really the assertion belongs in the engine
  506. // because one day, rotated collideables will work!
  507. Assert( pCollision->GetCollisionAngles() == vec3_angle );
  508. CTraceFilterEntity traceFilter( pEntity, pCollision->GetCollisionGroup() );
  509. #ifdef PORTAL
  510. UTIL_Portal_TraceEntity( pEntity, vecAbsStart, vecAbsEnd, mask, &traceFilter, ptr );
  511. #else
  512. enginetrace->SweepCollideable( pCollision, vecAbsStart, vecAbsEnd, pCollision->GetCollisionAngles(), mask, &traceFilter, ptr );
  513. #endif
  514. }
  515. void UTIL_TraceEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd,
  516. unsigned int mask, const IHandleEntity *pIgnore, int nCollisionGroup, trace_t *ptr )
  517. {
  518. ICollideable *pCollision;
  519. pCollision = pEntity->GetCollideable();
  520. // Adding this assertion here so game code catches it, but really the assertion belongs in the engine
  521. // because one day, rotated collideables will work!
  522. Assert( pCollision->GetCollisionAngles() == vec3_angle );
  523. CTraceFilterEntityIgnoreOther traceFilter( pEntity, pIgnore, nCollisionGroup );
  524. #ifdef PORTAL
  525. UTIL_Portal_TraceEntity( pEntity, vecAbsStart, vecAbsEnd, mask, &traceFilter, ptr );
  526. #else
  527. enginetrace->SweepCollideable( pCollision, vecAbsStart, vecAbsEnd, pCollision->GetCollisionAngles(), mask, &traceFilter, ptr );
  528. #endif
  529. }
  530. void UTIL_TraceEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd,
  531. unsigned int mask, ITraceFilter *pFilter, trace_t *ptr )
  532. {
  533. ICollideable *pCollision;
  534. pCollision = pEntity->GetCollideable();
  535. // Adding this assertion here so game code catches it, but really the assertion belongs in the engine
  536. // because one day, rotated collideables will work!
  537. Assert( pCollision->GetCollisionAngles() == vec3_angle );
  538. #ifdef PORTAL
  539. UTIL_Portal_TraceEntity( pEntity, vecAbsStart, vecAbsEnd, mask, pFilter, ptr );
  540. #else
  541. enginetrace->SweepCollideable( pCollision, vecAbsStart, vecAbsEnd, pCollision->GetCollisionAngles(), mask, pFilter, ptr );
  542. #endif
  543. }
  544. // ----
  545. // This is basically a regular TraceLine that uses the FilterEntity filter.
  546. void UTIL_TraceLineFilterEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd,
  547. unsigned int mask, int nCollisionGroup, trace_t *ptr )
  548. {
  549. CTraceFilterEntity traceFilter( pEntity, nCollisionGroup );
  550. UTIL_TraceLine( vecAbsStart, vecAbsEnd, mask, &traceFilter, ptr );
  551. }
  552. void UTIL_ClipTraceToPlayers( const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask, ITraceFilter *filter, trace_t *tr )
  553. {
  554. trace_t playerTrace;
  555. Ray_t ray;
  556. float smallestFraction = tr->fraction;
  557. const float maxRange = 60.0f;
  558. ray.Init( vecAbsStart, vecAbsEnd );
  559. for ( int k = 1; k <= gpGlobals->maxClients; ++k )
  560. {
  561. CBasePlayer *player = UTIL_PlayerByIndex( k );
  562. if ( !player || !player->IsAlive() )
  563. continue;
  564. #ifdef CLIENT_DLL
  565. if ( player->IsDormant() )
  566. continue;
  567. #endif // CLIENT_DLL
  568. if ( filter && filter->ShouldHitEntity( player, mask ) == false )
  569. continue;
  570. float range = DistanceToRay( player->WorldSpaceCenter(), vecAbsStart, vecAbsEnd );
  571. if ( range < 0.0f || range > maxRange )
  572. continue;
  573. enginetrace->ClipRayToEntity( ray, mask|CONTENTS_HITBOX, player, &playerTrace );
  574. if ( playerTrace.fraction < smallestFraction )
  575. {
  576. // we shortened the ray - save off the trace
  577. *tr = playerTrace;
  578. smallestFraction = playerTrace.fraction;
  579. }
  580. }
  581. }
  582. //-----------------------------------------------------------------------------
  583. // Purpose: Make a tracer using a particle effect
  584. //-----------------------------------------------------------------------------
  585. void UTIL_ParticleTracer( const char *pszTracerEffectName, const Vector &vecStart, const Vector &vecEnd,
  586. int iEntIndex, int iAttachment, bool bWhiz )
  587. {
  588. int iParticleIndex = GetParticleSystemIndex( pszTracerEffectName );
  589. UTIL_Tracer( vecStart, vecEnd, iEntIndex, iAttachment, 0, bWhiz, "ParticleTracer", iParticleIndex );
  590. }
  591. //-----------------------------------------------------------------------------
  592. // Purpose: Make a tracer effect using the old, non-particle system, tracer effects.
  593. //-----------------------------------------------------------------------------
  594. void UTIL_Tracer( const Vector &vecStart, const Vector &vecEnd, int iEntIndex,
  595. int iAttachment, float flVelocity, bool bWhiz, const char *pCustomTracerName, int iParticleID )
  596. {
  597. CEffectData data;
  598. data.m_vStart = vecStart;
  599. data.m_vOrigin = vecEnd;
  600. #ifdef CLIENT_DLL
  601. data.m_hEntity = ClientEntityList().EntIndexToHandle( iEntIndex );
  602. #else
  603. data.m_nEntIndex = iEntIndex;
  604. #endif
  605. data.m_flScale = flVelocity;
  606. data.m_nHitBox = iParticleID;
  607. // Flags
  608. if ( bWhiz )
  609. {
  610. data.m_fFlags |= TRACER_FLAG_WHIZ;
  611. }
  612. if ( iAttachment != TRACER_DONT_USE_ATTACHMENT )
  613. {
  614. data.m_fFlags |= TRACER_FLAG_USEATTACHMENT;
  615. data.m_nAttachmentIndex = iAttachment;
  616. }
  617. // Fire it off
  618. if ( pCustomTracerName )
  619. {
  620. DispatchEffect( pCustomTracerName, data );
  621. }
  622. else
  623. {
  624. DispatchEffect( "Tracer", data );
  625. }
  626. }
  627. void UTIL_BloodDrips( const Vector &origin, const Vector &direction, int color, int amount )
  628. {
  629. if ( !UTIL_ShouldShowBlood( color ) )
  630. return;
  631. if ( color == DONT_BLEED || amount == 0 )
  632. return;
  633. if ( g_Language.GetInt() == LANGUAGE_GERMAN && color == BLOOD_COLOR_RED )
  634. color = 0;
  635. if ( g_pGameRules->IsMultiplayer() )
  636. {
  637. // scale up blood effect in multiplayer for better visibility
  638. amount *= 5;
  639. }
  640. if ( amount > 255 )
  641. amount = 255;
  642. if (color == BLOOD_COLOR_MECH)
  643. {
  644. g_pEffects->Sparks(origin);
  645. if (random->RandomFloat(0, 2) >= 1)
  646. {
  647. UTIL_Smoke(origin, random->RandomInt(10, 15), 10);
  648. }
  649. }
  650. else
  651. {
  652. // Normal blood impact
  653. UTIL_BloodImpact( origin, direction, color, amount );
  654. }
  655. }
  656. //-----------------------------------------------------------------------------
  657. // Purpose: Returns low violence settings
  658. //-----------------------------------------------------------------------------
  659. static ConVar violence_hblood( "violence_hblood","1", 0, "Draw human blood" );
  660. static ConVar violence_hgibs( "violence_hgibs","1", 0, "Show human gib entities" );
  661. static ConVar violence_ablood( "violence_ablood","1", 0, "Draw alien blood" );
  662. static ConVar violence_agibs( "violence_agibs","1", 0, "Show alien gib entities" );
  663. bool UTIL_IsLowViolence( void )
  664. {
  665. // These convars are no longer necessary -- the engine is the final arbiter of
  666. // violence settings -- but they're here for legacy support and for testing low
  667. // violence when the engine is in normal violence mode.
  668. if ( !violence_hblood.GetBool() || !violence_ablood.GetBool() || !violence_hgibs.GetBool() || !violence_agibs.GetBool() )
  669. return true;
  670. #ifdef TF_CLIENT_DLL
  671. // Use low violence if the local player has an item that allows them to see it (Pyro Goggles)
  672. if ( IsLocalPlayerUsingVisionFilterFlags( TF_VISION_FILTER_PYRO ) )
  673. {
  674. return true;
  675. }
  676. #endif
  677. return engine->IsLowViolence();
  678. }
  679. bool UTIL_ShouldShowBlood( int color )
  680. {
  681. if ( color != DONT_BLEED )
  682. {
  683. if ( color == BLOOD_COLOR_RED )
  684. {
  685. return violence_hblood.GetBool();
  686. }
  687. else
  688. {
  689. return violence_ablood.GetBool();
  690. }
  691. }
  692. return false;
  693. }
  694. //------------------------------------------------------------------------------
  695. // Purpose : Use trace to pass a specific decal type to the entity being decaled
  696. // Input :
  697. // Output :
  698. //------------------------------------------------------------------------------
  699. void UTIL_DecalTrace( trace_t *pTrace, char const *decalName )
  700. {
  701. if (pTrace->fraction == 1.0)
  702. return;
  703. CBaseEntity *pEntity = pTrace->m_pEnt;
  704. pEntity->DecalTrace( pTrace, decalName );
  705. }
  706. void UTIL_BloodDecalTrace( trace_t *pTrace, int bloodColor )
  707. {
  708. if ( UTIL_ShouldShowBlood( bloodColor ) )
  709. {
  710. if ( bloodColor == BLOOD_COLOR_RED )
  711. {
  712. UTIL_DecalTrace( pTrace, "Blood" );
  713. }
  714. else
  715. {
  716. UTIL_DecalTrace( pTrace, "YellowBlood" );
  717. }
  718. }
  719. }
  720. //-----------------------------------------------------------------------------
  721. // Purpose:
  722. // Input : &pos -
  723. // &dir -
  724. // color -
  725. // amount -
  726. //-----------------------------------------------------------------------------
  727. void UTIL_BloodImpact( const Vector &pos, const Vector &dir, int color, int amount )
  728. {
  729. CEffectData data;
  730. data.m_vOrigin = pos;
  731. data.m_vNormal = dir;
  732. data.m_flScale = (float)amount;
  733. data.m_nColor = (unsigned char)color;
  734. DispatchEffect( "bloodimpact", data );
  735. }
  736. bool UTIL_IsSpaceEmpty( CBaseEntity *pMainEnt, const Vector &vMin, const Vector &vMax )
  737. {
  738. Vector vHalfDims = ( vMax - vMin ) * 0.5f;
  739. Vector vCenter = vMin + vHalfDims;
  740. trace_t trace;
  741. UTIL_TraceHull( vCenter, vCenter, -vHalfDims, vHalfDims, MASK_SOLID, pMainEnt, COLLISION_GROUP_NONE, &trace );
  742. bool bClear = ( trace.fraction == 1 && trace.allsolid != 1 && (trace.startsolid != 1) );
  743. return bClear;
  744. }
  745. void UTIL_StringToFloatArray( float *pVector, int count, const char *pString )
  746. {
  747. char *pstr, *pfront, tempString[128];
  748. int j;
  749. Q_strncpy( tempString, pString, sizeof(tempString) );
  750. pstr = pfront = tempString;
  751. for ( j = 0; j < count; j++ ) // lifted from pr_edict.c
  752. {
  753. pVector[j] = atof( pfront );
  754. // skip any leading whitespace
  755. while ( *pstr && *pstr <= ' ' )
  756. pstr++;
  757. // skip to next whitespace
  758. while ( *pstr && *pstr > ' ' )
  759. pstr++;
  760. if (!*pstr)
  761. break;
  762. pstr++;
  763. pfront = pstr;
  764. }
  765. for ( j++; j < count; j++ )
  766. {
  767. pVector[j] = 0;
  768. }
  769. }
  770. void UTIL_StringToVector( float *pVector, const char *pString )
  771. {
  772. UTIL_StringToFloatArray( pVector, 3, pString );
  773. }
  774. void UTIL_StringToIntArray( int *pVector, int count, const char *pString )
  775. {
  776. char *pstr, *pfront, tempString[128];
  777. int j;
  778. Q_strncpy( tempString, pString, sizeof(tempString) );
  779. pstr = pfront = tempString;
  780. for ( j = 0; j < count; j++ ) // lifted from pr_edict.c
  781. {
  782. pVector[j] = atoi( pfront );
  783. while ( *pstr && *pstr != ' ' )
  784. pstr++;
  785. if (!*pstr)
  786. break;
  787. pstr++;
  788. pfront = pstr;
  789. }
  790. for ( j++; j < count; j++ )
  791. {
  792. pVector[j] = 0;
  793. }
  794. }
  795. void UTIL_StringToColor32( color32 *color, const char *pString )
  796. {
  797. int tmp[4];
  798. UTIL_StringToIntArray( tmp, 4, pString );
  799. color->r = tmp[0];
  800. color->g = tmp[1];
  801. color->b = tmp[2];
  802. color->a = tmp[3];
  803. }
  804. #ifndef _XBOX
  805. void UTIL_DecodeICE( unsigned char * buffer, int size, const unsigned char *key)
  806. {
  807. if ( !key )
  808. return;
  809. IceKey ice( 0 ); // level 0 = 64bit key
  810. ice.set( key ); // set key
  811. int blockSize = ice.blockSize();
  812. unsigned char *temp = (unsigned char *)_alloca( PAD_NUMBER( size, blockSize ) );
  813. unsigned char *p1 = buffer;
  814. unsigned char *p2 = temp;
  815. // encrypt data in 8 byte blocks
  816. int bytesLeft = size;
  817. while ( bytesLeft >= blockSize )
  818. {
  819. ice.decrypt( p1, p2 );
  820. bytesLeft -= blockSize;
  821. p1+=blockSize;
  822. p2+=blockSize;
  823. }
  824. // copy encrypted data back to original buffer
  825. Q_memcpy( buffer, temp, size-bytesLeft );
  826. }
  827. #endif
  828. // work-around since client header doesn't like inlined gpGlobals->curtime
  829. float IntervalTimer::Now( void ) const
  830. {
  831. return gpGlobals->curtime;
  832. }
  833. // work-around since client header doesn't like inlined gpGlobals->curtime
  834. float CountdownTimer::Now( void ) const
  835. {
  836. return gpGlobals->curtime;
  837. }
  838. #ifdef CLIENT_DLL
  839. CBasePlayer *UTIL_PlayerByIndex( int entindex )
  840. {
  841. return ToBasePlayer( ClientEntityList().GetEnt( entindex ) );
  842. }
  843. #endif
  844. CBasePlayer *UTIL_PlayerBySteamID( const CSteamID &steamID )
  845. {
  846. CSteamID steamIDPlayer;
  847. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  848. {
  849. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  850. if ( !pPlayer )
  851. continue;
  852. if ( !pPlayer->GetSteamID( &steamIDPlayer ) )
  853. continue;
  854. if ( steamIDPlayer == steamID )
  855. return pPlayer;
  856. }
  857. return NULL;
  858. }
  859. // Helper for use with console commands and the like.
  860. // Returns NULL if not found or if the provided arg would match multiple players.
  861. // Currently accepts, in descending priority:
  862. // - Formatted SteamID ([U:1:1234])
  863. // - SteamID64 (76561197989728462)
  864. // - Legacy SteamID (STEAM_0:1:1234)
  865. // - UserID preceded by a pound (#4)
  866. // - Partial name match (if unique)
  867. // - UserID not preceded by a pound*
  868. //
  869. // *Does not count as ambiguous with higher priority items
  870. CBasePlayer* UTIL_PlayerByCommandArg( const char *arg )
  871. {
  872. size_t nLength = V_strlen( arg );
  873. if ( nLength < 1 )
  874. { return NULL; }
  875. // Is the argument numeric?
  876. bool bAllButFirstNumbers = true;
  877. for ( size_t idx = 1; bAllButFirstNumbers && idx < nLength; idx++ )
  878. {
  879. bAllButFirstNumbers = V_isdigit( arg[idx] );
  880. }
  881. bool bAllNumbers = V_isdigit( arg[0] ) && bAllButFirstNumbers;
  882. // Keep searching when we find a match to track ambiguous results
  883. CBasePlayer *pFound = NULL;
  884. // Assign pFound unless we already found a different player, in which case return NULL due to ambiguous
  885. // WTB Lambdas
  886. #define UTIL_PLAYERBYCMDARG_CHECKMATCH( pEvalMatch ) \
  887. do \
  888. { \
  889. CBasePlayer *_pMacroMatch = (pEvalMatch); \
  890. if ( _pMacroMatch ) \
  891. { \
  892. /* Ambiguity check */ \
  893. if ( pFound && pFound != _pMacroMatch ) \
  894. { return NULL; } \
  895. pFound = _pMacroMatch; \
  896. } \
  897. } while ( false );
  898. // Formatted SteamID or SteamID64
  899. if ( bAllNumbers || ( arg[0] == '[' && arg[nLength-1] == ']' ) )
  900. {
  901. CSteamID steamID;
  902. bool bMatch = steamID.SetFromStringStrict( arg, GetUniverse() );
  903. UTIL_PLAYERBYCMDARG_CHECKMATCH( bMatch ? UTIL_PlayerBySteamID( steamID ) : NULL );
  904. }
  905. // Legacy SteamID?
  906. const char szPrefix[] = "STEAM_";
  907. if ( nLength >= V_ARRAYSIZE( szPrefix ) && V_strncmp( szPrefix, arg, V_ARRAYSIZE( szPrefix ) - 1 ) == 0 )
  908. {
  909. CSteamID steamID;
  910. bool bMatch = steamID.SetFromSteam2String( arg, GetUniverse() );
  911. UTIL_PLAYERBYCMDARG_CHECKMATCH( bMatch ? UTIL_PlayerBySteamID( steamID ) : NULL );
  912. }
  913. // UserID preceded by a pound (#4)
  914. if ( nLength > 1 && arg[0] == '#' && bAllButFirstNumbers )
  915. {
  916. UTIL_PLAYERBYCMDARG_CHECKMATCH( UTIL_PlayerByUserId( V_atoi( arg + 1 ) ) );
  917. }
  918. // Partial name match (if unique)
  919. UTIL_PLAYERBYCMDARG_CHECKMATCH( UTIL_PlayerByPartialName( arg ) );
  920. // UserID not preceded by a pound
  921. // *Does not count as ambiguous with higher priority items
  922. if ( bAllNumbers && !pFound )
  923. {
  924. UTIL_PLAYERBYCMDARG_CHECKMATCH( UTIL_PlayerByUserId( V_atoi( arg ) ) );
  925. }
  926. return pFound;
  927. #undef UTIL_PLAYERBYCMDARG_CHECKMATCH
  928. }
  929. CBasePlayer* UTIL_PlayerByName( const char *name )
  930. {
  931. if ( !name || !name[0] )
  932. return NULL;
  933. for (int i = 1; i<=gpGlobals->maxClients; i++ )
  934. {
  935. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  936. if ( !pPlayer )
  937. continue;
  938. #ifndef CLIENT_DLL
  939. if ( !pPlayer->IsConnected() )
  940. continue;
  941. #endif
  942. if ( Q_stricmp( pPlayer->GetPlayerName(), name ) == 0 )
  943. {
  944. return pPlayer;
  945. }
  946. }
  947. return NULL;
  948. }
  949. // Finds a player who has this non-ambiguous substring
  950. CBasePlayer* UTIL_PlayerByPartialName( const char *name )
  951. {
  952. if ( !name || !name[0] )
  953. return NULL;
  954. CBasePlayer *pFound = NULL;
  955. for (int i = 1; i<=gpGlobals->maxClients; i++ )
  956. {
  957. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  958. if ( !pPlayer )
  959. continue;
  960. #ifndef CLIENT_DLL
  961. if ( !pPlayer->IsConnected() )
  962. continue;
  963. #endif
  964. if ( Q_stristr( pPlayer->GetPlayerName(), name ) )
  965. {
  966. if ( pFound )
  967. {
  968. // Ambiguous
  969. return NULL;
  970. }
  971. pFound = pPlayer;
  972. }
  973. }
  974. return pFound;
  975. }
  976. CBasePlayer* UTIL_PlayerByUserId( int userID )
  977. {
  978. for (int i = 1; i<=gpGlobals->maxClients; i++ )
  979. {
  980. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  981. if ( !pPlayer )
  982. continue;
  983. #ifndef CLIENT_DLL
  984. if ( !pPlayer->IsConnected() )
  985. continue;
  986. #endif
  987. if ( pPlayer->GetUserID() == userID )
  988. {
  989. return pPlayer;
  990. }
  991. }
  992. return NULL;
  993. }
  994. char* ReadAndAllocStringValue( KeyValues *pSub, const char *pName, const char *pFilename )
  995. {
  996. const char *pValue = pSub->GetString( pName, NULL );
  997. if ( !pValue )
  998. {
  999. if ( pFilename )
  1000. {
  1001. DevWarning( "Can't get key value '%s' from file '%s'.\n", pName, pFilename );
  1002. }
  1003. return "";
  1004. }
  1005. int len = Q_strlen( pValue ) + 1;
  1006. char *pAlloced = new char[ len ];
  1007. Assert( pAlloced );
  1008. Q_strncpy( pAlloced, pValue, len );
  1009. return pAlloced;
  1010. }
  1011. int UTIL_StringFieldToInt( const char *szValue, const char **pValueStrings, int iNumStrings )
  1012. {
  1013. if ( !szValue || !szValue[0] )
  1014. return -1;
  1015. for ( int i = 0; i < iNumStrings; i++ )
  1016. {
  1017. if ( FStrEq(szValue, pValueStrings[i]) )
  1018. return i;
  1019. }
  1020. Assert(0);
  1021. return -1;
  1022. }
  1023. int find_day_of_week( struct tm& found_day, int day_of_week, int step )
  1024. {
  1025. return 0;
  1026. }
  1027. //-----------------------------------------------------------------------------
  1028. // Purpose:
  1029. //-----------------------------------------------------------------------------
  1030. #ifdef USES_ECON_ITEMS
  1031. static bool s_HolidaysCalculated = false;
  1032. static CBitVec<kHolidayCount> s_HolidaysActive;
  1033. //-----------------------------------------------------------------------------
  1034. // Purpose: Used at level change and round start to re-calculate which holiday is active
  1035. //-----------------------------------------------------------------------------
  1036. void UTIL_CalculateHolidays()
  1037. {
  1038. s_HolidaysActive.ClearAll();
  1039. CRTime::UpdateRealTime();
  1040. for ( int iHoliday = 0; iHoliday < kHolidayCount; iHoliday++ )
  1041. {
  1042. if ( EconHolidays_IsHolidayActive( iHoliday, CRTime::RTime32TimeCur() ) )
  1043. {
  1044. s_HolidaysActive.Set( iHoliday );
  1045. }
  1046. }
  1047. s_HolidaysCalculated = true;
  1048. }
  1049. #endif // USES_ECON_ITEMS
  1050. bool UTIL_IsHolidayActive( /*EHoliday*/ int eHoliday )
  1051. {
  1052. #ifdef USES_ECON_ITEMS
  1053. if ( IsX360() )
  1054. return false;
  1055. if ( !s_HolidaysCalculated )
  1056. {
  1057. UTIL_CalculateHolidays();
  1058. }
  1059. return s_HolidaysActive.IsBitSet( eHoliday );
  1060. #else
  1061. return false;
  1062. #endif
  1063. }
  1064. //-----------------------------------------------------------------------------
  1065. // Purpose:
  1066. //-----------------------------------------------------------------------------
  1067. int UTIL_GetHolidayForString( const char* pszHolidayName )
  1068. {
  1069. #ifdef USES_ECON_ITEMS
  1070. if ( !pszHolidayName )
  1071. return kHoliday_None;
  1072. return EconHolidays_GetHolidayForString( pszHolidayName );
  1073. #else
  1074. return 0;
  1075. #endif
  1076. }
  1077. //-----------------------------------------------------------------------------
  1078. // Purpose:
  1079. //-----------------------------------------------------------------------------
  1080. const char* UTIL_GetActiveHolidayString()
  1081. {
  1082. #ifdef USES_ECON_ITEMS
  1083. return EconHolidays_GetActiveHolidayString();
  1084. #else
  1085. return NULL;
  1086. #endif
  1087. }
  1088. extern ISoundEmitterSystemBase *soundemitterbase;
  1089. //-----------------------------------------------------------------------------
  1090. // Purpose:
  1091. //-----------------------------------------------------------------------------
  1092. const char *UTIL_GetRandomSoundFromEntry( const char* pszEntryName )
  1093. {
  1094. Assert( pszEntryName );
  1095. if ( pszEntryName )
  1096. {
  1097. int soundIndex = soundemitterbase->GetSoundIndex( pszEntryName );
  1098. CSoundParametersInternal *internal = ( soundIndex != -1 ) ? soundemitterbase->InternalGetParametersForSound( soundIndex ) : NULL;
  1099. // See if we need to pick a random one
  1100. if ( internal )
  1101. {
  1102. int wave = RandomInt( 0, internal->NumSoundNames() - 1 );
  1103. pszEntryName = soundemitterbase->GetWaveName( internal->GetSoundNames()[wave].symbol );
  1104. }
  1105. }
  1106. return pszEntryName;
  1107. }
  1108. /// Clamp and round float vals to int. The values are in the 0...255 range.
  1109. Color FloatRGBAToColor( float r, float g, float b, float a )
  1110. {
  1111. return Color(
  1112. (unsigned char)clamp(r + .5f, 0.0, 255.0f),
  1113. (unsigned char)clamp(g + .5f, 0.0, 255.0f),
  1114. (unsigned char)clamp(b + .5f, 0.0, 255.0f),
  1115. (unsigned char)clamp(a + .5f, 0.0, 255.0f)
  1116. );
  1117. }
  1118. float LerpFloat( float x0, float x1, float t )
  1119. {
  1120. return x0 + (x1 - x0) * t;
  1121. }
  1122. Color LerpColor( const Color &c0, const Color &c1, float t )
  1123. {
  1124. if ( t <= 0.0f ) return c0;
  1125. if ( t >= 1.0f ) return c1;
  1126. return FloatRGBAToColor(
  1127. LerpFloat( (float)c0.r(), (float)c1.r(), t ),
  1128. LerpFloat( (float)c0.g(), (float)c1.g(), t ),
  1129. LerpFloat( (float)c0.b(), (float)c1.b(), t ),
  1130. LerpFloat( (float)c0.a(), (float)c1.a(), t )
  1131. );
  1132. }
  1133. ISteamUtils* GetSteamUtils()
  1134. {
  1135. #ifdef GAME_DLL
  1136. // Use steamgameserver context if this isn't a client/listenserver.
  1137. if ( engine->IsDedicatedServer() )
  1138. {
  1139. return steamgameserverapicontext ? steamgameserverapicontext->SteamGameServerUtils() : NULL;
  1140. }
  1141. #endif
  1142. return steamapicontext ? steamapicontext->SteamUtils() : NULL;
  1143. }
  1144. //-----------------------------------------------------------------------------
  1145. // Purpose:
  1146. //-----------------------------------------------------------------------------
  1147. EUniverse GetUniverse()
  1148. {
  1149. if ( !GetSteamUtils() )
  1150. return k_EUniverseInvalid;
  1151. static EUniverse steamUniverse = GetSteamUtils()->GetConnectedUniverse();
  1152. return steamUniverse;
  1153. }