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.

3518 lines
101 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Utility code.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "saverestore.h"
  9. #include "globalstate.h"
  10. #include <stdarg.h>
  11. #include "shake.h"
  12. #include "decals.h"
  13. #include "player.h"
  14. #include "gamerules.h"
  15. #include "entitylist.h"
  16. #include "bspfile.h"
  17. #include "mathlib/mathlib.h"
  18. #include "IEffects.h"
  19. #include "vstdlib/random.h"
  20. #include "soundflags.h"
  21. #include "ispatialpartition.h"
  22. #include "igamesystem.h"
  23. #include "saverestoretypes.h"
  24. #include "checksum_crc.h"
  25. #include "hierarchy.h"
  26. #include "iservervehicle.h"
  27. #include "te_effect_dispatch.h"
  28. #include "utldict.h"
  29. #include "collisionutils.h"
  30. #include "movevars_shared.h"
  31. #include "inetchannelinfo.h"
  32. #include "tier0/vprof.h"
  33. #include "ndebugoverlay.h"
  34. #include "engine/ivdebugoverlay.h"
  35. #include "datacache/imdlcache.h"
  36. #include "util.h"
  37. #include "usermessages.h"
  38. #ifdef PORTAL
  39. #include "portal_base2d_shared.h"
  40. #endif
  41. #include "CegClientWrapper.h"
  42. // memdbgon must be the last include file in a .cpp file!!!
  43. #include "tier0/memdbgon.h"
  44. extern int g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud
  45. extern int g_sModelIndexBloodDrop; // (in combatweapon.cpp) holds the sprite index for the initial blood
  46. extern int g_sModelIndexBloodSpray; // (in combatweapon.cpp) holds the sprite index for splattered blood
  47. // this is true if the engine should be sent log output. Once per frame it is rechecked to see if logging has been enabled.
  48. bool g_bIsLogging = true;
  49. #ifdef DEBUG
  50. void DBG_AssertFunction( bool fExpr, const char *szExpr, const char *szFile, int szLine, const char *szMessage )
  51. {
  52. if (fExpr)
  53. return;
  54. char szOut[512];
  55. if (szMessage != NULL)
  56. Q_snprintf(szOut,sizeof(szOut), "ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage);
  57. else
  58. Q_snprintf(szOut,sizeof(szOut), "ASSERT FAILED:\n %s \n(%s@%d)\n", szExpr, szFile, szLine);
  59. Warning( "%s", szOut);
  60. }
  61. #endif // DEBUG
  62. //-----------------------------------------------------------------------------
  63. // Entity creation factory
  64. //-----------------------------------------------------------------------------
  65. class CEntityFactoryDictionary : public IEntityFactoryDictionary
  66. {
  67. public:
  68. CEntityFactoryDictionary();
  69. virtual void InstallFactory( IEntityFactory *pFactory, const char *pClassName );
  70. virtual IServerNetworkable *Create( const char *pClassName );
  71. virtual void Destroy( const char *pClassName, IServerNetworkable *pNetworkable );
  72. virtual const char *GetCannonicalName( const char *pClassName );
  73. void ReportEntitySizes();
  74. private:
  75. IEntityFactory *FindFactory( const char *pClassName );
  76. public:
  77. CUtlDict< IEntityFactory *, unsigned short > m_Factories;
  78. };
  79. //-----------------------------------------------------------------------------
  80. // Singleton accessor
  81. //-----------------------------------------------------------------------------
  82. IEntityFactoryDictionary *EntityFactoryDictionary()
  83. {
  84. static CEntityFactoryDictionary s_EntityFactory;
  85. return &s_EntityFactory;
  86. }
  87. void DumpEntityFactories_f()
  88. {
  89. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  90. return;
  91. CEntityFactoryDictionary *dict = ( CEntityFactoryDictionary * )EntityFactoryDictionary();
  92. if ( dict )
  93. {
  94. for ( int i = dict->m_Factories.First(); i != dict->m_Factories.InvalidIndex(); i = dict->m_Factories.Next( i ) )
  95. {
  96. Warning( "%s\n", dict->m_Factories.GetElementName( i ) );
  97. }
  98. }
  99. }
  100. static ConCommand dumpentityfactories( "dumpentityfactories", DumpEntityFactories_f, "Lists all entity factory names.", FCVAR_GAMEDLL );
  101. //-----------------------------------------------------------------------------
  102. //
  103. //-----------------------------------------------------------------------------
  104. CON_COMMAND( dump_entity_sizes, "Print sizeof(entclass)" )
  105. {
  106. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  107. return;
  108. ((CEntityFactoryDictionary*)EntityFactoryDictionary())->ReportEntitySizes();
  109. }
  110. //-----------------------------------------------------------------------------
  111. // Constructor
  112. //-----------------------------------------------------------------------------
  113. CEntityFactoryDictionary::CEntityFactoryDictionary() : m_Factories( true, 0, 128 )
  114. {
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Finds a new factory
  118. //-----------------------------------------------------------------------------
  119. IEntityFactory *CEntityFactoryDictionary::FindFactory( const char *pClassName )
  120. {
  121. unsigned short nIndex = m_Factories.Find( pClassName );
  122. if ( nIndex == m_Factories.InvalidIndex() )
  123. return NULL;
  124. return m_Factories[nIndex];
  125. }
  126. //-----------------------------------------------------------------------------
  127. // Install a new factory
  128. //-----------------------------------------------------------------------------
  129. void CEntityFactoryDictionary::InstallFactory( IEntityFactory *pFactory, const char *pClassName )
  130. {
  131. AssertMsg1( FindFactory( pClassName ) == NULL, "Double installation of factory for %s", pClassName );
  132. m_Factories.Insert( pClassName, pFactory );
  133. }
  134. //-----------------------------------------------------------------------------
  135. // Instantiate something using a factory
  136. //-----------------------------------------------------------------------------
  137. IServerNetworkable *CEntityFactoryDictionary::Create( const char *pClassName )
  138. {
  139. IEntityFactory *pFactory = FindFactory( pClassName );
  140. if ( !pFactory )
  141. {
  142. Warning("Attempted to create unknown entity type %s!\n", pClassName );
  143. return NULL;
  144. }
  145. #if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG)
  146. MEM_ALLOC_CREDIT_( m_Factories.GetElementName( m_Factories.Find( pClassName ) ) );
  147. #endif
  148. return pFactory->Create( pClassName );
  149. }
  150. //-----------------------------------------------------------------------------
  151. //
  152. //-----------------------------------------------------------------------------
  153. const char *CEntityFactoryDictionary::GetCannonicalName( const char *pClassName )
  154. {
  155. return m_Factories.GetElementName( m_Factories.Find( pClassName ) );
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Destroy a networkable
  159. //-----------------------------------------------------------------------------
  160. void CEntityFactoryDictionary::Destroy( const char *pClassName, IServerNetworkable *pNetworkable )
  161. {
  162. IEntityFactory *pFactory = FindFactory( pClassName );
  163. if ( !pFactory )
  164. {
  165. Warning("Attempted to destroy unknown entity type %s!\n", pClassName );
  166. return;
  167. }
  168. pFactory->Destroy( pNetworkable );
  169. }
  170. //-----------------------------------------------------------------------------
  171. //
  172. //-----------------------------------------------------------------------------
  173. void CEntityFactoryDictionary::ReportEntitySizes()
  174. {
  175. for ( int i = m_Factories.First(); i != m_Factories.InvalidIndex(); i = m_Factories.Next( i ) )
  176. {
  177. Msg( " %s: %d", m_Factories.GetElementName( i ), m_Factories[i]->GetEntitySize() );
  178. }
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Purpose:
  182. //-----------------------------------------------------------------------------
  183. int UTIL_PrecacheDecal( const char *name, bool preload )
  184. {
  185. // If this is out of order, make sure to warn.
  186. if ( !CBaseEntity::IsPrecacheAllowed() )
  187. {
  188. if ( !engine->IsDecalPrecached( name ) )
  189. {
  190. Assert( !"UTIL_PrecacheDecal: too late" );
  191. Warning( "Late precache of %s\n", name );
  192. }
  193. }
  194. return engine->PrecacheDecal( name, preload );
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Purpose:
  198. //-----------------------------------------------------------------------------
  199. float UTIL_GetSimulationInterval()
  200. {
  201. if ( CBaseEntity::IsSimulatingOnAlternateTicks() )
  202. return ( TICK_INTERVAL * 2.0 );
  203. return TICK_INTERVAL;
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Purpose:
  207. //-----------------------------------------------------------------------------
  208. int UTIL_EntitiesInBox( const Vector &mins, const Vector &maxs, CFlaggedEntitiesEnum *pEnum )
  209. {
  210. ::partition->EnumerateElementsInBox( PARTITION_ENGINE_NON_STATIC_EDICTS, mins, maxs, false, pEnum );
  211. return pEnum->GetCount();
  212. }
  213. int UTIL_EntitiesInSphere( const Vector &center, float radius, CFlaggedEntitiesEnum *pEnum )
  214. {
  215. ::partition->EnumerateElementsInSphere( PARTITION_ENGINE_NON_STATIC_EDICTS, center, radius, false, pEnum );
  216. return pEnum->GetCount();
  217. }
  218. CEntitySphereQuery::CEntitySphereQuery( const Vector &center, float radius, int flagMask )
  219. {
  220. m_listIndex = 0;
  221. m_listCount = UTIL_EntitiesInSphere( m_pList, ARRAYSIZE(m_pList), center, radius, flagMask );
  222. }
  223. CBaseEntity *CEntitySphereQuery::GetCurrentEntity()
  224. {
  225. if ( m_listIndex < m_listCount )
  226. return m_pList[m_listIndex];
  227. return NULL;
  228. }
  229. //-----------------------------------------------------------------------------
  230. // Simple trace filter
  231. //-----------------------------------------------------------------------------
  232. class CTracePassFilter : public CTraceFilter
  233. {
  234. public:
  235. CTracePassFilter( IHandleEntity *pPassEnt ) : m_pPassEnt( pPassEnt ) {}
  236. bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  237. {
  238. if ( !StandardFilterRules( pHandleEntity, contentsMask ) )
  239. return false;
  240. if (!PassServerEntityFilter( pHandleEntity, m_pPassEnt ))
  241. return false;
  242. return true;
  243. }
  244. private:
  245. IHandleEntity *m_pPassEnt;
  246. };
  247. //-----------------------------------------------------------------------------
  248. // Drops an entity onto the floor
  249. //-----------------------------------------------------------------------------
  250. int UTIL_DropToFloor( CBaseEntity *pEntity, unsigned int mask, CBaseEntity *pIgnore)
  251. {
  252. // Assume no ground
  253. pEntity->SetGroundEntity( NULL );
  254. Assert( pEntity );
  255. trace_t trace;
  256. // HACK: is this really the only sure way to detect crossing a terrain boundry?
  257. UTIL_TraceEntity( pEntity, pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin(), mask, pIgnore, pEntity->GetCollisionGroup(), &trace );
  258. if (trace.fraction == 0.0)
  259. return -1;
  260. UTIL_TraceEntity( pEntity, pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin() - Vector(0,0,256), mask, pIgnore, pEntity->GetCollisionGroup(), &trace );
  261. if (trace.allsolid)
  262. return -1;
  263. if (trace.fraction == 1)
  264. return 0;
  265. pEntity->SetAbsOrigin( trace.endpos );
  266. pEntity->SetGroundEntity( trace.m_pEnt );
  267. return 1;
  268. }
  269. //-----------------------------------------------------------------------------
  270. // Returns false if any part of the bottom of the entity is off an edge that
  271. // is not a staircase.
  272. //-----------------------------------------------------------------------------
  273. bool UTIL_CheckBottom( CBaseEntity *pEntity, ITraceFilter *pTraceFilter, float flStepSize )
  274. {
  275. Vector mins, maxs, start, stop;
  276. trace_t trace;
  277. int x, y;
  278. float mid, bottom;
  279. Assert( pEntity );
  280. CTracePassFilter traceFilter(pEntity);
  281. if ( !pTraceFilter )
  282. {
  283. pTraceFilter = &traceFilter;
  284. }
  285. unsigned int mask = pEntity->PhysicsSolidMaskForEntity();
  286. VectorAdd (pEntity->GetAbsOrigin(), pEntity->WorldAlignMins(), mins);
  287. VectorAdd (pEntity->GetAbsOrigin(), pEntity->WorldAlignMaxs(), maxs);
  288. // if all of the points under the corners are solid world, don't bother
  289. // with the tougher checks
  290. // the corners must be within 16 of the midpoint
  291. start[2] = mins[2] - 1;
  292. for (x=0 ; x<=1 ; x++)
  293. {
  294. for (y=0 ; y<=1 ; y++)
  295. {
  296. start[0] = x ? maxs[0] : mins[0];
  297. start[1] = y ? maxs[1] : mins[1];
  298. if (enginetrace->GetPointContents(start) != CONTENTS_SOLID)
  299. goto realcheck;
  300. }
  301. }
  302. return true; // we got out easy
  303. realcheck:
  304. // check it for real...
  305. start[2] = mins[2] + flStepSize; // seems to help going up/down slopes.
  306. // the midpoint must be within 16 of the bottom
  307. start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
  308. start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
  309. stop[2] = start[2] - 2*flStepSize;
  310. UTIL_TraceLine( start, stop, mask, pTraceFilter, &trace );
  311. if (trace.fraction == 1.0)
  312. return false;
  313. mid = bottom = trace.endpos[2];
  314. // the corners must be within 16 of the midpoint
  315. for (x=0 ; x<=1 ; x++)
  316. {
  317. for (y=0 ; y<=1 ; y++)
  318. {
  319. start[0] = stop[0] = x ? maxs[0] : mins[0];
  320. start[1] = stop[1] = y ? maxs[1] : mins[1];
  321. UTIL_TraceLine( start, stop, mask, pTraceFilter, &trace );
  322. if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
  323. bottom = trace.endpos[2];
  324. if (trace.fraction == 1.0 || mid - trace.endpos[2] > flStepSize)
  325. return false;
  326. }
  327. }
  328. return true;
  329. }
  330. bool g_bDisableEhandleAccess = false;
  331. bool g_bReceivedChainedUpdateOnRemove = false;
  332. //-----------------------------------------------------------------------------
  333. // Purpose: Sets the entity up for deletion. Entity will not actually be deleted
  334. // until the next frame, so there can be no pointer errors.
  335. // Input : *oldObj - object to delete
  336. //-----------------------------------------------------------------------------
  337. void UTIL_Remove( IServerNetworkable *oldObj )
  338. {
  339. CServerNetworkProperty* pProp = static_cast<CServerNetworkProperty*>( oldObj );
  340. if ( !pProp || pProp->IsMarkedForDeletion() )
  341. return;
  342. if ( PhysIsInCallback() )
  343. {
  344. // This assert means that someone is deleting an entity inside a callback. That isn't supported so
  345. // this code will defer the deletion of that object until the end of the current physics simulation frame
  346. // Since this is hidden from the calling code it's preferred to call PhysCallbackRemove() directly from the caller
  347. // in case the deferred delete will have unwanted results (like continuing to receive callbacks). That will make it
  348. // obvious why the unwanted results are happening so the caller can handle them appropriately. (some callbacks can be masked
  349. // or the calling entity can be flagged to filter them in most cases)
  350. Assert(0);
  351. PhysCallbackRemove(oldObj);
  352. return;
  353. }
  354. // mark it for deletion
  355. pProp->MarkForDeletion( );
  356. CBaseEntity *pBaseEnt = oldObj->GetBaseEntity();
  357. if ( pBaseEnt )
  358. {
  359. #ifdef PORTAL //make sure entities are in the primary physics environment for the portal mod, this code should be safe even if the entity is in neither extra environment
  360. CPortalSimulator::Pre_UTIL_Remove( pBaseEnt );
  361. #endif
  362. g_bReceivedChainedUpdateOnRemove = false;
  363. pBaseEnt->UpdateOnRemove();
  364. Assert( g_bReceivedChainedUpdateOnRemove );
  365. // clear oldObj targetname / other flags now
  366. pBaseEnt->SetName( NULL_STRING );
  367. #ifdef PORTAL
  368. CPortalSimulator::Post_UTIL_Remove( pBaseEnt );
  369. #endif
  370. }
  371. gEntList.AddToDeleteList( oldObj );
  372. }
  373. void UTIL_Remove( CBaseEntity *oldObj )
  374. {
  375. if ( !oldObj )
  376. return;
  377. UTIL_Remove( oldObj->NetworkProp() );
  378. }
  379. static int s_RemoveImmediateSemaphore = 0;
  380. void UTIL_DisableRemoveImmediate()
  381. {
  382. s_RemoveImmediateSemaphore++;
  383. }
  384. void UTIL_EnableRemoveImmediate()
  385. {
  386. s_RemoveImmediateSemaphore--;
  387. Assert(s_RemoveImmediateSemaphore>=0);
  388. }
  389. //-----------------------------------------------------------------------------
  390. // Purpose: deletes an entity, without any delay. WARNING! Only use this when sure
  391. // no pointers rely on this entity.
  392. // Input : *oldObj - the entity to delete
  393. //-----------------------------------------------------------------------------
  394. void UTIL_RemoveImmediate( CBaseEntity *oldObj )
  395. {
  396. // valid pointer or already removed?
  397. if ( !oldObj || oldObj->IsEFlagSet(EFL_KILLME) )
  398. return;
  399. if ( s_RemoveImmediateSemaphore )
  400. {
  401. UTIL_Remove(oldObj);
  402. return;
  403. }
  404. #ifdef PORTAL //make sure entities are in the primary physics environment for the portal mod, this code should be safe even if the entity is in neither extra environment
  405. CPortalSimulator::Pre_UTIL_Remove( oldObj );
  406. #endif
  407. oldObj->AddEFlags( EFL_KILLME ); // Make sure to ignore further calls into here or UTIL_Remove.
  408. g_bReceivedChainedUpdateOnRemove = false;
  409. oldObj->UpdateOnRemove();
  410. Assert( g_bReceivedChainedUpdateOnRemove );
  411. // Entities shouldn't reference other entities in their destructors
  412. // that type of code should only occur in an UpdateOnRemove call
  413. g_bDisableEhandleAccess = true;
  414. delete oldObj;
  415. g_bDisableEhandleAccess = false;
  416. #ifdef PORTAL
  417. CPortalSimulator::Post_UTIL_Remove( oldObj );
  418. #endif
  419. }
  420. // returns a CBaseEntity pointer to a player by index. Only returns if the player is spawned and connected
  421. // otherwise returns NULL
  422. // Index is 1 based
  423. CBasePlayer *UTIL_PlayerByIndex( int playerIndex )
  424. {
  425. CBasePlayer *pPlayer = NULL;
  426. if ( playerIndex > 0 && playerIndex <= gpGlobals->maxClients )
  427. {
  428. edict_t *pPlayerEdict = INDEXENT( playerIndex );
  429. if ( pPlayerEdict && !pPlayerEdict->IsFree() )
  430. {
  431. pPlayer = (CBasePlayer*)GetContainingEntity( pPlayerEdict );
  432. }
  433. }
  434. return pPlayer;
  435. }
  436. CBasePlayer* UTIL_PlayerByName( const char *name )
  437. {
  438. if ( !name || !name[0] )
  439. return NULL;
  440. for (int i = 1; i<=gpGlobals->maxClients; i++ )
  441. {
  442. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  443. if ( !pPlayer )
  444. continue;
  445. if ( !pPlayer->IsConnected() )
  446. continue;
  447. if ( Q_stricmp( pPlayer->GetPlayerName(), name ) == 0 )
  448. {
  449. return pPlayer;
  450. }
  451. }
  452. return NULL;
  453. }
  454. CBasePlayer* UTIL_PlayerByUserId( int userID )
  455. {
  456. for (int i = 1; i<=gpGlobals->maxClients; i++ )
  457. {
  458. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  459. if ( !pPlayer )
  460. continue;
  461. if ( !pPlayer->IsConnected() )
  462. continue;
  463. if ( engine->GetPlayerUserId(pPlayer->edict()) == userID )
  464. {
  465. return pPlayer;
  466. }
  467. }
  468. return NULL;
  469. }
  470. CBasePlayer* UTIL_PlayerByAccountID( AccountID_t accountID )
  471. {
  472. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  473. {
  474. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  475. if ( !pPlayer )
  476. continue;
  477. if ( !pPlayer->IsConnected( ) )
  478. continue;
  479. if ( engine->GetClientSteamID( pPlayer->edict( ) ) &&
  480. ( engine->GetClientSteamID( pPlayer->edict( ) )->GetAccountID( ) == accountID ) )
  481. {
  482. return pPlayer;
  483. }
  484. }
  485. return NULL;
  486. }
  487. //
  488. // Return the local player.
  489. // If this is a multiplayer game, return NULL.
  490. //
  491. CBasePlayer *UTIL_GetLocalPlayer( void )
  492. {
  493. if ( gpGlobals->maxClients > 1 )
  494. {
  495. if ( developer.GetBool() )
  496. {
  497. Assert( !"UTIL_GetLocalPlayer" );
  498. #ifdef DEBUG
  499. Warning( "UTIL_GetLocalPlayer() called in multiplayer game.\n" );
  500. #endif
  501. }
  502. return NULL;
  503. }
  504. return UTIL_PlayerByIndex( 1 );
  505. }
  506. //
  507. // Get the local player on a listen server - this is for multiplayer use only
  508. //
  509. CBasePlayer *UTIL_GetListenServerHost( void )
  510. {
  511. // no "local player" if this is a dedicated server or a single player game
  512. if (engine->IsDedicatedServer())
  513. {
  514. Assert( !"UTIL_GetListenServerHost" );
  515. Warning( "UTIL_GetListenServerHost() called from a dedicated server or single-player game.\n" );
  516. return NULL;
  517. }
  518. return UTIL_PlayerByIndex( 1 );
  519. }
  520. //--------------------------------------------------------------------------------------------------------------
  521. /**
  522. * Returns true if the command was issued by the listenserver host, or by the dedicated server, via rcon or the server console.
  523. * This is valid during ConCommand execution.
  524. */
  525. bool UTIL_IsCommandIssuedByServerAdmin( void )
  526. {
  527. int issuingPlayerIndex = UTIL_GetCommandClientIndex();
  528. if ( engine->IsDedicatedServer() )
  529. {
  530. if ( issuingPlayerIndex > 0 )
  531. return false;
  532. }
  533. else
  534. {
  535. if ( issuingPlayerIndex != engine->GetLocalClientIndex() ) // comparing 1-based client index, also called Player Index in some places
  536. return false;
  537. }
  538. return true;
  539. }
  540. //--------------------------------------------------------------------------------------------------------------
  541. /**
  542. * Returns a CBaseEntity pointer by entindex. Index is 1 based.
  543. */
  544. CBaseEntity *UTIL_EntityByIndex( int entityIndex )
  545. {
  546. CBaseEntity *entity = NULL;
  547. if ( entityIndex > 0 )
  548. {
  549. edict_t *edict = INDEXENT( entityIndex );
  550. if ( edict && !edict->IsFree() )
  551. {
  552. entity = GetContainingEntity( edict );
  553. }
  554. }
  555. return entity;
  556. }
  557. int ENTINDEX( CBaseEntity *pEnt )
  558. {
  559. // This works just like ENTINDEX for edicts.
  560. if ( pEnt )
  561. return pEnt->entindex();
  562. else
  563. return 0;
  564. }
  565. //-----------------------------------------------------------------------------
  566. // Purpose:
  567. // Input : playerIndex -
  568. // ping -
  569. // packetloss -
  570. //-----------------------------------------------------------------------------
  571. void UTIL_GetPlayerConnectionInfo( int playerIndex, int& ping, int &packetloss )
  572. {
  573. CBasePlayer *player = UTIL_PlayerByIndex( playerIndex );
  574. if ( player->IsSplitScreenPlayer() &&
  575. player->GetSplitScreenPlayerOwner() )
  576. {
  577. player = player->GetSplitScreenPlayerOwner();
  578. playerIndex = player->entindex();
  579. }
  580. INetChannelInfo *nci = engine->GetPlayerNetInfo(playerIndex);
  581. if ( nci && player && !player->IsBot() )
  582. {
  583. float latency = nci->GetAvgLatency( FLOW_OUTGOING ); // in seconds
  584. // that should be the correct latency, we assume that cmdrate is higher
  585. // then updaterate, what is the case for default settings
  586. const char * szCmdRate = engine->GetClientConVarValue( playerIndex, "cl_cmdrate" );
  587. const char * szUpdateRate = engine->GetClientConVarValue( playerIndex, "cl_updaterate" );
  588. static const ConVar *pSvClientCmdrateDifference = g_pCVar->FindVar( "sv_client_cmdrate_difference" );
  589. static const ConVar *pMinUpdateRate = g_pCVar->FindVar( "sv_minupdaterate" );
  590. static const ConVar *pMaxUpdateRate = g_pCVar->FindVar( "sv_maxupdaterate" );
  591. float flUpdateRateValue = Q_atof( szUpdateRate );
  592. if ( !player->IsHLTV() )
  593. {
  594. if ( pMinUpdateRate && pMaxUpdateRate )
  595. flUpdateRateValue = clamp( flUpdateRateValue, pMinUpdateRate->GetFloat(), pMaxUpdateRate->GetFloat() );
  596. }
  597. float flCmdRateValue = Q_atof( szCmdRate );
  598. if ( player->IsHLTV() )
  599. {
  600. flCmdRateValue = flUpdateRateValue;
  601. }
  602. else
  603. {
  604. // First, we make it stay within range of cl_updaterate.
  605. if ( pSvClientCmdrateDifference )
  606. {
  607. float diff = flCmdRateValue - flUpdateRateValue;
  608. float diffMaxCap = pSvClientCmdrateDifference->GetFloat();
  609. if ( fabs( diff ) > diffMaxCap )
  610. {
  611. if ( diff > 0 )
  612. flCmdRateValue = flUpdateRateValue + diffMaxCap;
  613. else
  614. flCmdRateValue = flUpdateRateValue - diffMaxCap;
  615. }
  616. }
  617. static const ConVar *pMinCmdRate = g_pCVar->FindVar( "sv_mincmdrate" );
  618. static const ConVar *pMaxCmdRate = g_pCVar->FindVar( "sv_maxcmdrate" );
  619. if ( pMinUpdateRate && pMaxUpdateRate )
  620. flCmdRateValue = clamp( flCmdRateValue, pMinCmdRate->GetFloat(), pMaxCmdRate->GetFloat() );
  621. }
  622. latency -= (0.5f/flCmdRateValue) + TICKS_TO_TIME( 1.0f ); // correct latency
  623. // in GoldSrc we had a different, not fixed tickrate. so we have to adjust
  624. // Source pings by half a tick to match the old GoldSrc pings.
  625. latency -= TICKS_TO_TIME( 0.5f );
  626. ping = latency * 1000.0f; // as msecs
  627. ping = clamp( ping, 5, 1000 ); // set bounds, dont show pings under 5 msecs
  628. packetloss = 100.0f * nci->GetAvgLoss( FLOW_INCOMING ); // loss in percentage
  629. packetloss = clamp( packetloss, 0, 100 );
  630. }
  631. else
  632. {
  633. ping = 0;
  634. packetloss = 0;
  635. }
  636. }
  637. static unsigned short FixedUnsigned16( float value, float scale )
  638. {
  639. int output;
  640. output = value * scale;
  641. if ( output < 0 )
  642. output = 0;
  643. if ( output > 0xFFFF )
  644. output = 0xFFFF;
  645. return (unsigned short)output;
  646. }
  647. //-----------------------------------------------------------------------------
  648. // Compute shake amplitude
  649. //-----------------------------------------------------------------------------
  650. inline float ComputeShakeAmplitude( const Vector &center, const Vector &shakePt, float amplitude, float radius )
  651. {
  652. if ( radius <= 0 )
  653. return amplitude;
  654. float localAmplitude = -1;
  655. Vector delta = center - shakePt;
  656. float distance = delta.Length();
  657. if ( distance <= radius )
  658. {
  659. // Make the amplitude fall off over distance
  660. float flPerc = 1.0 - (distance / radius);
  661. localAmplitude = amplitude * flPerc;
  662. }
  663. return localAmplitude;
  664. }
  665. //-----------------------------------------------------------------------------
  666. // Transmits the actual shake event
  667. //-----------------------------------------------------------------------------
  668. inline void TransmitShakeEvent( CBasePlayer *pPlayer, float localAmplitude, float frequency, float duration, ShakeCommand_t eCommand )
  669. {
  670. if (( localAmplitude > 0 ) || ( eCommand == SHAKE_STOP ))
  671. {
  672. if ( eCommand == SHAKE_STOP )
  673. localAmplitude = 0;
  674. CSingleUserRecipientFilter user( pPlayer );
  675. user.MakeReliable();
  676. CCSUsrMsg_Shake msg;
  677. msg.set_command( eCommand ); // shake command (SHAKE_START, STOP, FREQUENCY, AMPLITUDE)
  678. msg.set_local_amplitude( localAmplitude ); // shake magnitude/amplitude
  679. msg.set_frequency( frequency ); // shake noise frequency
  680. msg.set_duration( duration ); // shake lasts this long
  681. SendUserMessage( user, CS_UM_Shake, msg );
  682. }
  683. }
  684. //-----------------------------------------------------------------------------
  685. // Purpose: Shake the screen of all clients within radius.
  686. // radius == 0, shake all clients
  687. // UNDONE: Fix falloff model (disabled)?
  688. // UNDONE: Affect user controls?
  689. // Input : center - Center of screen shake, radius is measured from here.
  690. // amplitude - Amplitude of shake
  691. // frequency -
  692. // duration - duration of shake in seconds.
  693. // radius - Radius of effect, 0 shakes all clients.
  694. // command - One of the following values:
  695. // SHAKE_START - starts the screen shake for all players within the radius
  696. // SHAKE_STOP - stops the screen shake for all players within the radius
  697. // SHAKE_AMPLITUDE - modifies the amplitude of the screen shake
  698. // for all players within the radius
  699. // SHAKE_FREQUENCY - modifies the frequency of the screen shake
  700. // for all players within the radius
  701. // bAirShake - if this is false, then it will only shake players standing on the ground.
  702. //-----------------------------------------------------------------------------
  703. const float MAX_SHAKE_AMPLITUDE = 16.0f;
  704. void UTIL_ScreenShake( const Vector &center, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake, CUtlVector<CBasePlayer *> *ignore )
  705. {
  706. int i;
  707. float localAmplitude;
  708. if ( amplitude > MAX_SHAKE_AMPLITUDE )
  709. {
  710. amplitude = MAX_SHAKE_AMPLITUDE;
  711. }
  712. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  713. {
  714. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  715. //
  716. // Only start shakes for players that are on the ground unless doing an air shake.
  717. //
  718. if ( !pPlayer || (!bAirShake && (eCommand == SHAKE_START) && !(pPlayer->GetFlags() & FL_ONGROUND)) )
  719. {
  720. continue;
  721. }
  722. if ( ignore && ignore->HasElement( pPlayer ) )
  723. {
  724. continue;
  725. }
  726. localAmplitude = ComputeShakeAmplitude( center, pPlayer->WorldSpaceCenter(), amplitude, radius );
  727. // This happens if the player is outside the radius, in which case we should ignore
  728. // all commands
  729. if (localAmplitude < 0)
  730. continue;
  731. TransmitShakeEvent( pPlayer, localAmplitude, frequency, duration, eCommand );
  732. }
  733. }
  734. //-----------------------------------------------------------------------------
  735. // Purpose: Shake an object and all players on or near it
  736. //-----------------------------------------------------------------------------
  737. void UTIL_ScreenShakeObject( CBaseEntity *pEnt, const Vector &center, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake )
  738. {
  739. int i;
  740. float localAmplitude;
  741. CBaseEntity *pHighestParent = pEnt->GetRootMoveParent();
  742. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  743. {
  744. CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
  745. if (!pPlayer)
  746. continue;
  747. // Shake the object, or anything hierarchically attached to it at maximum amplitude
  748. localAmplitude = 0;
  749. if (pHighestParent == pPlayer->GetRootMoveParent())
  750. {
  751. localAmplitude = amplitude;
  752. }
  753. else if ((pPlayer->GetFlags() & FL_ONGROUND) && pPlayer->GetGroundEntity() && (pPlayer->GetGroundEntity()->GetRootMoveParent() == pHighestParent))
  754. {
  755. // If the player is standing on the object, use maximum amplitude
  756. localAmplitude = amplitude;
  757. }
  758. else
  759. {
  760. // Only shake players that are on the ground.
  761. if ( !bAirShake && !(pPlayer->GetFlags() & FL_ONGROUND) )
  762. {
  763. continue;
  764. }
  765. localAmplitude = ComputeShakeAmplitude( center, pPlayer->WorldSpaceCenter(), amplitude, radius );
  766. // This happens if the player is outside the radius,
  767. // in which case we should ignore all commands
  768. if (localAmplitude < 0)
  769. continue;
  770. }
  771. TransmitShakeEvent( (CBasePlayer *)pPlayer, localAmplitude, frequency, duration, eCommand );
  772. }
  773. }
  774. //-----------------------------------------------------------------------------
  775. // Transmits the actual tilt event
  776. //-----------------------------------------------------------------------------
  777. inline void TransmitTiltEvent( CBasePlayer *pPlayer, QAngle tiltAngle, float duration, float tiltTime, ShakeCommand_t eCommand, bool bEaseInOut )
  778. {
  779. // Gurjeets - Not used in csgo
  780. //CSingleUserRecipientFilter user( pPlayer );
  781. //user.MakeReliable();
  782. //UserMessageBegin( user, "Tilt" );
  783. //WRITE_BYTE( eCommand ); // tilt command (SHAKE_START, STOP, FREQUENCY, AMPLITUDE)
  784. //WRITE_BYTE( bEaseInOut ); // tilt ease in/out
  785. //WRITE_FLOAT( tiltAngle.x ); // tilt angle
  786. //WRITE_FLOAT( tiltAngle.y );
  787. //WRITE_FLOAT( tiltAngle.z );
  788. //WRITE_FLOAT( duration ); // tilt lasts this long
  789. //WRITE_FLOAT( tiltTime ); // tilt time
  790. //MessageEnd();
  791. }
  792. //-----------------------------------------------------------------------------
  793. // Purpose: Tilt the screen of all clients within radius.
  794. // radius == 0, shake all clients
  795. // Input : center - Center of screen tilt, radius is measured from here.
  796. // tiltAngle - Angle that the world is pretending to tilt
  797. // duration - duration of tilt in seconds.
  798. // radius - Radius of effect, 0 shakes all clients.
  799. // tiltTime - how long it takes to reach full tilt
  800. // command - One of the following values:
  801. // SHAKE_START - starts the screen tilt for all players within the radius
  802. // SHAKE_STOP - stops the screen tilt for all players within the radius
  803. // SHAKE_AMPLITUDE - modifies the amplitude of the screen tilt
  804. // for all players within the radius
  805. // SHAKE_FREQUENCY - modifies the frequency of the screen tilt
  806. // for all players within the radius
  807. // bAirShake - if this is false, then it will only tilt players standing on the ground.
  808. //-----------------------------------------------------------------------------
  809. void UTIL_ScreenTilt( const Vector &center, const QAngle &tiltAngle, float duration, float radius, float tiltTime, ShakeCommand_t eCommand, bool bEaseInOut )
  810. {
  811. int i;
  812. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  813. {
  814. CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
  815. //
  816. // Only start shakes for players that are on the ground unless doing an air shake.
  817. //
  818. if ( !pPlayer )
  819. {
  820. continue;
  821. }
  822. // This happens if the player is outside the radius, in which case we should ignore
  823. // all commands
  824. if ( radius != 0.0f && pPlayer->WorldSpaceCenter().DistTo( center ) > radius )
  825. continue;
  826. TransmitTiltEvent( (CBasePlayer *)pPlayer, tiltAngle, duration, tiltTime, eCommand, bEaseInOut );
  827. }
  828. }
  829. //-----------------------------------------------------------------------------
  830. // Purpose: Punches the view of all clients within radius.
  831. // If radius is 0, punches all clients.
  832. // Input : center - Center of punch, radius is measured from here.
  833. // radius - Radius of effect, 0 punches all clients.
  834. // bInAir - if this is false, then it will only punch players standing on the ground.
  835. //-----------------------------------------------------------------------------
  836. void UTIL_ViewPunch( const Vector &center, QAngle angPunch, float radius, bool bInAir )
  837. {
  838. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  839. {
  840. CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
  841. //
  842. // Only apply the punch to players that are on the ground unless doing an air punch.
  843. //
  844. if ( !pPlayer || (!bInAir && !(pPlayer->GetFlags() & FL_ONGROUND)) )
  845. {
  846. continue;
  847. }
  848. QAngle angTemp = angPunch;
  849. if ( radius > 0 )
  850. {
  851. Vector delta = center - pPlayer->GetAbsOrigin();
  852. float distance = delta.Length();
  853. if ( distance <= radius )
  854. {
  855. // Make the punch amplitude fall off over distance.
  856. float flPerc = 1.0 - (distance / radius);
  857. angTemp *= flPerc;
  858. }
  859. else
  860. {
  861. continue;
  862. }
  863. }
  864. pPlayer->ViewPunch( angTemp );
  865. }
  866. }
  867. void UTIL_ScreenFadeBuild( ScreenFade_t &fade, const color32 &color, float fadeTime, float fadeHold, int flags )
  868. {
  869. fade.duration = FixedUnsigned16( fadeTime, 1<<SCREENFADE_FRACBITS ); // 7.9 fixed
  870. fade.holdTime = FixedUnsigned16( fadeHold, 1<<SCREENFADE_FRACBITS ); // 7.9 fixed
  871. fade.r = color.r;
  872. fade.g = color.g;
  873. fade.b = color.b;
  874. fade.a = color.a;
  875. fade.fadeFlags = flags;
  876. }
  877. void UTIL_ScreenFadeWrite( const ScreenFade_t &fade, CBaseEntity *pEntity )
  878. {
  879. if ( !pEntity || !pEntity->IsNetClient() )
  880. return;
  881. CBasePlayer *pRecipient = static_cast< CBasePlayer * >( pEntity );
  882. if ( pRecipient->ShouldThrottleUserMessage( "Fade" ) )
  883. {
  884. return;
  885. }
  886. CSingleUserRecipientFilter user( pRecipient );
  887. user.MakeReliable();
  888. CCSUsrMsg_Fade msg;
  889. msg.set_duration( fade.duration ); // fade lasts this long
  890. msg.set_hold_time( fade.holdTime ); // fade lasts this long
  891. msg.set_flags( fade.fadeFlags ); // fade type (in / out)
  892. msg.mutable_clr()->set_r( fade.r ); // fade red
  893. msg.mutable_clr()->set_g( fade.g ); // fade green
  894. msg.mutable_clr()->set_b( fade.b ); // fade blue
  895. msg.mutable_clr()->set_a( fade.a ); // fade blue
  896. SendUserMessage( user, CS_UM_Fade, msg );
  897. }
  898. void UTIL_ScreenFadeAll( const color32 &color, float fadeTime, float fadeHold, int flags )
  899. {
  900. int i;
  901. ScreenFade_t fade;
  902. UTIL_ScreenFadeBuild( fade, color, fadeTime, fadeHold, flags );
  903. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  904. {
  905. CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
  906. UTIL_ScreenFadeWrite( fade, pPlayer );
  907. }
  908. }
  909. void UTIL_ScreenFade( CBaseEntity *pEntity, const color32 &color, float fadeTime, float fadeHold, int flags )
  910. {
  911. ScreenFade_t fade;
  912. UTIL_ScreenFadeBuild( fade, color, fadeTime, fadeHold, flags );
  913. UTIL_ScreenFadeWrite( fade, pEntity );
  914. }
  915. void UTIL_HudMessage( CBasePlayer *pToPlayer, const hudtextparms_t &textparms, const char *pMessage )
  916. {
  917. CRecipientFilter filter;
  918. if( pToPlayer )
  919. {
  920. filter.AddRecipient( pToPlayer );
  921. }
  922. else
  923. {
  924. filter.AddAllPlayers();
  925. }
  926. filter.MakeReliable();
  927. CCSUsrMsg_HudMsg msg;
  928. msg.set_channel( textparms.channel & 0xFF );
  929. msg.mutable_pos()->set_x( textparms.x );
  930. msg.mutable_pos()->set_y( textparms.y );
  931. msg.mutable_clr1()->set_r( textparms.r1 );
  932. msg.mutable_clr1()->set_g( textparms.g1 );
  933. msg.mutable_clr1()->set_b( textparms.b1 );
  934. msg.mutable_clr1()->set_a( textparms.a1 );
  935. msg.mutable_clr2()->set_r( textparms.r2 );
  936. msg.mutable_clr2()->set_g( textparms.g2 );
  937. msg.mutable_clr2()->set_b( textparms.b2 );
  938. msg.mutable_clr2()->set_a( textparms.a2 );
  939. msg.set_effect ( textparms.effect );
  940. msg.set_fade_in_time( textparms.fadeinTime );
  941. msg.set_fade_out_time( textparms.fadeoutTime );
  942. msg.set_hold_time( textparms.holdTime );
  943. msg.set_fx_time( textparms.fxTime );
  944. msg.set_text( pMessage );
  945. SendUserMessage( filter, CS_UM_HudMsg, msg );
  946. }
  947. void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage )
  948. {
  949. UTIL_HudMessage( NULL, textparms, pMessage );
  950. }
  951. void UTIL_HudHintText( CBaseEntity *pEntity, const char *pMessage )
  952. {
  953. if ( !pEntity )
  954. return;
  955. CSingleUserRecipientFilter user( (CBasePlayer *)pEntity );
  956. user.MakeReliable();
  957. CCSUsrMsg_KeyHintText msg;
  958. msg.add_hints( pMessage );
  959. SendUserMessage( user, CS_UM_KeyHintText, msg );
  960. }
  961. void UTIL_ClientPrintFilter( IRecipientFilter& filter, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 )
  962. {
  963. CCSUsrMsg_TextMsg msg;
  964. msg.set_msg_dst( msg_dest );
  965. msg.add_params( msg_name );
  966. if ( param1 )
  967. msg.add_params( param1 );
  968. else
  969. msg.add_params( "" );
  970. if ( param2 )
  971. msg.add_params( param2 );
  972. else
  973. msg.add_params( "" );
  974. if ( param3 )
  975. msg.add_params( param3 );
  976. else
  977. msg.add_params( "" );
  978. if ( param4 )
  979. msg.add_params( param4 );
  980. else
  981. msg.add_params( "" );
  982. SendUserMessage( filter, CS_UM_TextMsg, msg );
  983. }
  984. void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 )
  985. {
  986. CReliableBroadcastRecipientFilter filter;
  987. UTIL_ClientPrintFilter( filter, msg_dest, msg_name, param1, param2, param3, param4 );
  988. }
  989. void ClientPrint( CBasePlayer *player, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 )
  990. {
  991. if ( !player )
  992. return;
  993. CSingleUserRecipientFilter user( player );
  994. user.MakeReliable();
  995. UTIL_ClientPrintFilter( user, msg_dest, msg_name, param1, param2, param3, param4 );
  996. }
  997. void UTIL_SayTextFilter( IRecipientFilter& filter, const char *pText, CBasePlayer *pPlayer, EUtilSayTextMessageType_t eMessageType )
  998. {
  999. CCSUsrMsg_SayText msg;
  1000. if ( pPlayer )
  1001. {
  1002. msg.set_ent_idx( pPlayer->entindex() );
  1003. }
  1004. else
  1005. {
  1006. msg.set_ent_idx( 0 ); // world, dedicated server says
  1007. }
  1008. msg.set_text( pText );
  1009. msg.set_chat( ( eMessageType == kEUtilSayTextMessageType_TeamonlyChat ) || ( eMessageType == kEUtilSayTextMessageType_AllChat ) );
  1010. msg.set_textallchat( eMessageType == kEUtilSayTextMessageType_AllChat );
  1011. SendUserMessage( filter, CS_UM_SayText, msg );
  1012. }
  1013. void UTIL_SayText2Filter( IRecipientFilter& filter, CBasePlayer *pEntity, EUtilSayTextMessageType_t eMessageType, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 )
  1014. {
  1015. CCSUsrMsg_SayText2 msg;
  1016. if ( pEntity )
  1017. {
  1018. msg.set_ent_idx( pEntity->entindex() );
  1019. }
  1020. else
  1021. {
  1022. msg.set_ent_idx( 0 ); // world, dedicated server says
  1023. }
  1024. msg.set_chat( ( eMessageType == kEUtilSayTextMessageType_TeamonlyChat ) || ( eMessageType == kEUtilSayTextMessageType_AllChat ) );
  1025. msg.set_textallchat( eMessageType == kEUtilSayTextMessageType_AllChat );
  1026. msg.set_msg_name( msg_name );
  1027. if ( param1 )
  1028. msg.add_params( param1 );
  1029. else
  1030. msg.add_params( "" );
  1031. if ( param2 )
  1032. msg.add_params( param2 );
  1033. else
  1034. msg.add_params( "" );
  1035. if ( param3 )
  1036. msg.add_params( param3 );
  1037. else
  1038. msg.add_params( "" );
  1039. if ( param4 )
  1040. msg.add_params( param4 );
  1041. else
  1042. msg.add_params( "" );
  1043. SendUserMessage( filter, CS_UM_SayText2, msg );
  1044. }
  1045. void UTIL_SayText( const char *pText, CBasePlayer *pToPlayer )
  1046. {
  1047. if ( !pToPlayer->IsNetClient() )
  1048. return;
  1049. CSingleUserRecipientFilter user( pToPlayer );
  1050. user.MakeReliable();
  1051. UTIL_SayTextFilter( user, pText, pToPlayer, kEUtilSayTextMessageType_Default );
  1052. }
  1053. void UTIL_SayTextAll( const char *pText, CBasePlayer *pPlayer, EUtilSayTextMessageType_t eMessageType )
  1054. {
  1055. CReliableBroadcastRecipientFilter filter;
  1056. UTIL_SayTextFilter( filter, pText, pPlayer, eMessageType );
  1057. }
  1058. void UTIL_ShowMessage( const char *pString, CBasePlayer *pPlayer )
  1059. {
  1060. CRecipientFilter filter;
  1061. if ( pPlayer )
  1062. {
  1063. filter.AddRecipient( pPlayer );
  1064. }
  1065. else
  1066. {
  1067. filter.AddAllPlayers();
  1068. }
  1069. filter.MakeReliable();
  1070. CCSUsrMsg_HudText msg;
  1071. msg.set_text( pString );
  1072. SendUserMessage( filter, CS_UM_HudText, msg );
  1073. }
  1074. void UTIL_ShowMessageAll( const char *pString )
  1075. {
  1076. UTIL_ShowMessage( pString, NULL );
  1077. }
  1078. //-------------------------------------------------------------------------------------------------
  1079. // HudMessagePanel helper
  1080. void UTIL_MessageTextAll( const char *text, Color color )
  1081. {
  1082. // Gurjeets - Not used in CSGO
  1083. //CReliableBroadcastRecipientFilter filter;
  1084. //UserMessageBegin( filter, "MessageText" );
  1085. // WRITE_BYTE( color.r() );
  1086. // WRITE_BYTE( color.g() );
  1087. // WRITE_BYTE( color.b() );
  1088. // WRITE_STRING( text );
  1089. //MessageEnd();
  1090. }
  1091. //-------------------------------------------------------------------------------------------------
  1092. // HudMessagePanel helper
  1093. void UTIL_MessageText( CBasePlayer *player, const char *text, Color color )
  1094. {
  1095. // Gurjeets - Not used in CSGO
  1096. //CSingleUserRecipientFilter filter( player );
  1097. //filter.MakeReliable();
  1098. //UserMessageBegin( filter, "MessageText" );
  1099. // WRITE_BYTE( color.r() );
  1100. // WRITE_BYTE( color.g() );
  1101. // WRITE_BYTE( color.b() );
  1102. // WRITE_STRING( text );
  1103. //MessageEnd();
  1104. }
  1105. //-------------------------------------------------------------------------------------------------
  1106. // HudMessagePanel helper
  1107. void UTIL_ResetMessageTextAll( void )
  1108. {
  1109. // Gurjeets - Not used in CSGO
  1110. //CReliableBroadcastRecipientFilter filter;
  1111. //UserMessageBegin( filter, "MessageText" );
  1112. //MessageEnd();
  1113. }
  1114. //-------------------------------------------------------------------------------------------------
  1115. // HudMessagePanel helper
  1116. void UTIL_ResetMessageText( CBasePlayer *player )
  1117. {
  1118. // Gurjeets - Not used in CSGO
  1119. //CSingleUserRecipientFilter filter( player );
  1120. //filter.MakeReliable();
  1121. //UserMessageBegin( filter, "MessageText" );
  1122. //MessageEnd();
  1123. }
  1124. // So we always return a valid surface
  1125. static csurface_t g_NullSurface = { "**empty**", 0 };
  1126. void UTIL_SetTrace(trace_t& trace, const Ray_t &ray, edict_t *ent, float fraction,
  1127. int hitgroup, unsigned int contents, const Vector& normal, float intercept )
  1128. {
  1129. trace.startsolid = (fraction == 0.0f);
  1130. trace.fraction = fraction;
  1131. VectorCopy( ray.m_Start, trace.startpos );
  1132. VectorMA( ray.m_Start, fraction, ray.m_Delta, trace.endpos );
  1133. VectorCopy( normal, trace.plane.normal );
  1134. trace.plane.dist = intercept;
  1135. trace.m_pEnt = CBaseEntity::Instance( ent );
  1136. trace.hitgroup = hitgroup;
  1137. trace.surface = g_NullSurface;
  1138. trace.contents = contents;
  1139. }
  1140. void UTIL_ClearTrace( trace_t &trace )
  1141. {
  1142. memset( &trace, 0, sizeof(trace));
  1143. trace.fraction = 1.f;
  1144. trace.fractionleftsolid = 0;
  1145. trace.surface = g_NullSurface;
  1146. }
  1147. //-----------------------------------------------------------------------------
  1148. // Sets the entity size
  1149. //-----------------------------------------------------------------------------
  1150. static void SetMinMaxSize (CBaseEntity *pEnt, const Vector& mins, const Vector& maxs )
  1151. {
  1152. for ( int i=0 ; i<3 ; i++ )
  1153. {
  1154. if ( mins[i] > maxs[i] )
  1155. {
  1156. Error( "%s: backwards mins/maxs", ( pEnt ) ? pEnt->GetDebugName() : "<NULL>" );
  1157. }
  1158. }
  1159. Assert( pEnt );
  1160. pEnt->SetCollisionBounds( mins, maxs );
  1161. }
  1162. //-----------------------------------------------------------------------------
  1163. // Sets the model size
  1164. //-----------------------------------------------------------------------------
  1165. void UTIL_SetSize( CBaseEntity *pEnt, const Vector &vecMin, const Vector &vecMax )
  1166. {
  1167. SetMinMaxSize (pEnt, vecMin, vecMax);
  1168. }
  1169. //-----------------------------------------------------------------------------
  1170. // Sets the model to be associated with an entity
  1171. //-----------------------------------------------------------------------------
  1172. void UTIL_SetModel( CBaseEntity *pEntity, const char *pModelName )
  1173. {
  1174. // check to see if model was properly precached
  1175. int i = modelinfo->GetModelIndex( pModelName );
  1176. if ( i < 0 )
  1177. {
  1178. Error("%i/%s - %s: UTIL_SetModel: not precached: %s\n", pEntity->entindex(),
  1179. STRING( pEntity->GetEntityName() ),
  1180. pEntity->GetClassname(), pModelName);
  1181. }
  1182. pEntity->SetModelIndex( i ) ;
  1183. pEntity->SetModelName( AllocPooledString( pModelName ) );
  1184. // brush model
  1185. const model_t *mod = modelinfo->GetModel( i );
  1186. if ( mod )
  1187. {
  1188. Vector mins, maxs;
  1189. modelinfo->GetModelBounds( mod, mins, maxs );
  1190. SetMinMaxSize (pEntity, mins, maxs);
  1191. }
  1192. else
  1193. {
  1194. SetMinMaxSize (pEntity, vec3_origin, vec3_origin);
  1195. }
  1196. CBaseAnimating *pAnimating = pEntity->GetBaseAnimating();
  1197. if ( pAnimating )
  1198. {
  1199. pAnimating->m_nForceBone = 0;
  1200. }
  1201. }
  1202. void UTIL_SetOrigin( CBaseEntity *entity, const Vector &vecOrigin, bool bFireTriggers )
  1203. {
  1204. entity->SetLocalOrigin( vecOrigin );
  1205. if ( bFireTriggers )
  1206. {
  1207. entity->PhysicsTouchTriggers();
  1208. }
  1209. }
  1210. void UTIL_ParticleEffect( const Vector &vecOrigin, const Vector &vecDirection, uint32 ulColor, int ulCount )
  1211. {
  1212. Msg( "UTIL_ParticleEffect: Disabled\n" );
  1213. }
  1214. void UTIL_Smoke( const Vector &origin, const float scale, const float framerate )
  1215. {
  1216. g_pEffects->Smoke( origin, g_sModelIndexSmoke, scale, framerate );
  1217. }
  1218. // snaps a vector to the nearest axis vector (if within epsilon)
  1219. void UTIL_SnapDirectionToAxis( Vector &direction, float epsilon )
  1220. {
  1221. float proj = 1 - epsilon;
  1222. for ( int i = 0; i < 3; i ++ )
  1223. {
  1224. if ( fabs(direction[i]) > proj )
  1225. {
  1226. // snap to axis unit vector
  1227. if ( direction[i] < 0 )
  1228. direction[i] = -1.0f;
  1229. else
  1230. direction[i] = 1.0f;
  1231. direction[(i+1)%3] = 0;
  1232. direction[(i+2)%3] = 0;
  1233. return;
  1234. }
  1235. }
  1236. }
  1237. const char *UTIL_VarArgs( const char *format, ... )
  1238. {
  1239. va_list argptr;
  1240. static char string[1024];
  1241. va_start (argptr, format);
  1242. Q_vsnprintf(string, sizeof(string), format,argptr);
  1243. va_end (argptr);
  1244. return string;
  1245. }
  1246. bool UTIL_IsMasterTriggered(string_t sMaster, CBaseEntity *pActivator)
  1247. {
  1248. if (sMaster != NULL_STRING)
  1249. {
  1250. CBaseEntity *pMaster = gEntList.FindEntityByName( NULL, sMaster, NULL, pActivator );
  1251. if ( pMaster && (pMaster->ObjectCaps() & FCAP_MASTER) )
  1252. {
  1253. return pMaster->IsTriggered( pActivator );
  1254. }
  1255. Warning( "Master was null or not a master!\n");
  1256. }
  1257. // if this isn't a master entity, just say yes.
  1258. return true;
  1259. }
  1260. void UTIL_BloodStream( const Vector &origin, const Vector &direction, int color, int amount )
  1261. {
  1262. if ( !UTIL_ShouldShowBlood( color ) )
  1263. return;
  1264. if ( g_Language.GetInt() == LANGUAGE_GERMAN && color == BLOOD_COLOR_RED )
  1265. color = 0;
  1266. CPVSFilter filter( origin );
  1267. te->BloodStream( filter, 0.0, &origin, &direction, 247, 63, 14, 255, MIN( amount, 255 ) );
  1268. }
  1269. Vector UTIL_RandomBloodVector( void )
  1270. {
  1271. Vector direction;
  1272. direction.x = random->RandomFloat ( -1, 1 );
  1273. direction.y = random->RandomFloat ( -1, 1 );
  1274. direction.z = random->RandomFloat ( 0, 1 );
  1275. return direction;
  1276. }
  1277. //------------------------------------------------------------------------------
  1278. // Purpose : Creates both an decal and any associated impact effects (such
  1279. // as flecks) for the given iDamageType and the trace's end position
  1280. // Input :
  1281. // Output :
  1282. //------------------------------------------------------------------------------
  1283. void UTIL_ImpactTrace( trace_t *pTrace, int iDamageType, char *pCustomImpactName )
  1284. {
  1285. CBaseEntity *pEntity = pTrace->m_pEnt;
  1286. // Is the entity valid, is the surface sky?
  1287. if ( !pEntity || !UTIL_IsValidEntity( pEntity ) || (pTrace->surface.flags & SURF_SKY) )
  1288. return;
  1289. if ( pTrace->fraction == 1.0 )
  1290. return;
  1291. pEntity->ImpactTrace( pTrace, iDamageType, pCustomImpactName );
  1292. }
  1293. /*
  1294. ==============
  1295. UTIL_PlayerDecalTrace
  1296. A player is trying to apply his custom decal for the spray can.
  1297. Tell connected clients to display it, or use the default spray can decal
  1298. if the custom can't be loaded.
  1299. ==============
  1300. */
  1301. void UTIL_PlayerDecalTrace( trace_t *pTrace, Vector const &right, int playernum )
  1302. {
  1303. if (pTrace->fraction == 1.0)
  1304. return;
  1305. CBroadcastRecipientFilter filter;
  1306. filter.SetIgnorePredictionCull( true ); // need to ignore prediction cull for the person who is spraying
  1307. te->PlayerDecal( filter, 0.0,
  1308. &pTrace->endpos, &pTrace->startpos, &right, playernum, pTrace->m_pEnt->entindex(), pTrace->hitbox );
  1309. }
  1310. bool UTIL_TeamsMatch( const char *pTeamName1, const char *pTeamName2 )
  1311. {
  1312. // Everyone matches unless it's teamplay
  1313. if ( !g_pGameRules->IsTeamplay() )
  1314. return true;
  1315. // Both on a team?
  1316. if ( *pTeamName1 != 0 && *pTeamName2 != 0 )
  1317. {
  1318. if ( !stricmp( pTeamName1, pTeamName2 ) ) // Same Team?
  1319. return true;
  1320. }
  1321. return false;
  1322. }
  1323. void UTIL_AxisStringToPointPoint( Vector &start, Vector &end, const char *pString )
  1324. {
  1325. char tmpstr[256];
  1326. Q_strncpy( tmpstr, pString, sizeof(tmpstr) );
  1327. char *pVec = strtok( tmpstr, "," );
  1328. int i = 0;
  1329. while ( pVec != NULL && *pVec )
  1330. {
  1331. if ( i == 0 )
  1332. {
  1333. UTIL_StringToVector( start.Base(), pVec );
  1334. i++;
  1335. }
  1336. else
  1337. {
  1338. UTIL_StringToVector( end.Base(), pVec );
  1339. }
  1340. pVec = strtok( NULL, "," );
  1341. }
  1342. }
  1343. void UTIL_AxisStringToPointDir( Vector &start, Vector &dir, const char *pString )
  1344. {
  1345. Vector end;
  1346. UTIL_AxisStringToPointPoint( start, end, pString );
  1347. dir = end - start;
  1348. VectorNormalize(dir);
  1349. }
  1350. void UTIL_AxisStringToUnitDir( Vector &dir, const char *pString )
  1351. {
  1352. Vector start;
  1353. UTIL_AxisStringToPointDir( start, dir, pString );
  1354. }
  1355. /*
  1356. ==================================================
  1357. UTIL_ClipPunchAngleOffset
  1358. ==================================================
  1359. */
  1360. void UTIL_ClipPunchAngleOffset( QAngle &in, const QAngle &punch, const QAngle &clip )
  1361. {
  1362. QAngle final = in + punch;
  1363. //Clip each component
  1364. for ( int i = 0; i < 3; i++ )
  1365. {
  1366. if ( final[i] > clip[i] )
  1367. {
  1368. final[i] = clip[i];
  1369. }
  1370. else if ( final[i] < -clip[i] )
  1371. {
  1372. final[i] = -clip[i];
  1373. }
  1374. //Return the result
  1375. in[i] = final[i] - punch[i];
  1376. }
  1377. }
  1378. extern int g_sModelIndexBubbles;// holds the index for the bubbles model
  1379. void UTIL_Bubbles( const Vector& mins, const Vector& maxs, int count )
  1380. {
  1381. Vector mid = (mins + maxs) * 0.5;
  1382. float flHeight = UTIL_WaterLevel( mid, mid.z, mid.z + 1024 );
  1383. flHeight = flHeight - mins.z;
  1384. CPASFilter filter( mid );
  1385. te->Bubbles( filter, 0.0,
  1386. &mins, &maxs, flHeight, g_sModelIndexBubbles, count, 8.0 );
  1387. }
  1388. void UTIL_BubbleTrail( const Vector& from, const Vector& to, int count )
  1389. {
  1390. // Find water surface will return from.z if the from point is above water
  1391. float flStartHeight = UTIL_FindWaterSurface( from, from.z, from.z + 256 );
  1392. flStartHeight = flStartHeight - from.z;
  1393. float flEndHeight = UTIL_FindWaterSurface( to, to.z, to.z + 256 );
  1394. flEndHeight = flEndHeight - to.z;
  1395. if ( ( flStartHeight == 0 ) && ( flEndHeight == 0 ) )
  1396. return;
  1397. float flWaterZ = flStartHeight + from.z;
  1398. const Vector *pFrom = &from;
  1399. const Vector *pTo = &to;
  1400. Vector vecWaterPoint;
  1401. if ( ( flStartHeight == 0 ) || ( flEndHeight == 0 ) )
  1402. {
  1403. if ( flStartHeight == 0 )
  1404. {
  1405. flWaterZ = flEndHeight + to.z;
  1406. }
  1407. float t = IntersectRayWithAAPlane( from, to, 2, 1.0f, flWaterZ );
  1408. Assert( (t >= -1e-3f) && ( t <= 1.0f ) );
  1409. VectorLerp( from, to, t, vecWaterPoint );
  1410. if ( flStartHeight == 0 )
  1411. {
  1412. pFrom = &vecWaterPoint;
  1413. // Reduce the count by the actual length
  1414. count = (int)( count * ( 1.0f - t ) );
  1415. }
  1416. else
  1417. {
  1418. pTo = &vecWaterPoint;
  1419. // Reduce the count by the actual length
  1420. count = (int)( count * t );
  1421. }
  1422. }
  1423. CBroadcastRecipientFilter filter;
  1424. te->BubbleTrail( filter, 0.0, pFrom, pTo, flWaterZ, g_sModelIndexBubbles, count, 8.0 );
  1425. }
  1426. //-----------------------------------------------------------------------------
  1427. // Purpose:
  1428. // Input : Start -
  1429. // End -
  1430. // ModelIndex -
  1431. // FrameStart -
  1432. // FrameRate -
  1433. // Life -
  1434. // Width -
  1435. // Noise -
  1436. // Red -
  1437. // Green -
  1438. // Brightness -
  1439. // Speed -
  1440. //-----------------------------------------------------------------------------
  1441. void UTIL_Beam( Vector &Start, Vector &End, int nModelIndex, int nHaloIndex, unsigned char FrameStart, unsigned char FrameRate,
  1442. float Life, unsigned char Width, unsigned char EndWidth, unsigned char FadeLength, unsigned char Noise, unsigned char Red, unsigned char Green,
  1443. unsigned char Blue, unsigned char Brightness, unsigned char Speed)
  1444. {
  1445. CBroadcastRecipientFilter filter;
  1446. te->BeamPoints( filter, 0.0,
  1447. &Start,
  1448. &End,
  1449. nModelIndex,
  1450. nHaloIndex,
  1451. FrameStart,
  1452. FrameRate,
  1453. Life,
  1454. Width,
  1455. EndWidth,
  1456. FadeLength,
  1457. Noise,
  1458. Red,
  1459. Green,
  1460. Blue,
  1461. Brightness,
  1462. Speed );
  1463. }
  1464. bool UTIL_IsValidEntity( CBaseEntity *pEnt )
  1465. {
  1466. edict_t *pEdict = pEnt->edict();
  1467. if ( !pEdict || pEdict->IsFree() )
  1468. return false;
  1469. return true;
  1470. }
  1471. #define PRECACHE_OTHER_ONCE
  1472. // UNDONE: Do we need this to avoid doing too much of this? Measure startup times and see
  1473. #if defined( PRECACHE_OTHER_ONCE )
  1474. #include "utlsymbol.h"
  1475. class CPrecacheOtherList : public CAutoGameSystem
  1476. {
  1477. public:
  1478. CPrecacheOtherList( char const *name ) : CAutoGameSystem( name )
  1479. {
  1480. }
  1481. virtual void LevelInitPreEntity();
  1482. virtual void LevelShutdownPostEntity();
  1483. bool AddOrMarkPrecached( const char *pClassname );
  1484. private:
  1485. CUtlSymbolTable m_list;
  1486. };
  1487. void CPrecacheOtherList::LevelInitPreEntity()
  1488. {
  1489. m_list.RemoveAll();
  1490. }
  1491. void CPrecacheOtherList::LevelShutdownPostEntity()
  1492. {
  1493. m_list.RemoveAll();
  1494. }
  1495. //-----------------------------------------------------------------------------
  1496. // Purpose: mark or add
  1497. // Input : *pEntity -
  1498. // Output : Returns true on success, false on failure.
  1499. //-----------------------------------------------------------------------------
  1500. bool CPrecacheOtherList::AddOrMarkPrecached( const char *pClassname )
  1501. {
  1502. CUtlSymbol sym = m_list.Find( pClassname );
  1503. if ( sym.IsValid() )
  1504. return false;
  1505. m_list.AddString( pClassname );
  1506. return true;
  1507. }
  1508. CPrecacheOtherList g_PrecacheOtherList( "CPrecacheOtherList" );
  1509. #endif
  1510. //-----------------------------------------------------------------------------
  1511. // Purpose:
  1512. // Input : *szClassname -
  1513. // *modelName -
  1514. //-----------------------------------------------------------------------------
  1515. void UTIL_PrecacheOther( const char *szClassname, const char *modelName )
  1516. {
  1517. #if defined( PRECACHE_OTHER_ONCE )
  1518. // already done this one?, if not, mark as done
  1519. if ( !g_PrecacheOtherList.AddOrMarkPrecached( szClassname ) )
  1520. return;
  1521. #endif
  1522. CBaseEntity *pEntity = CreateEntityByName( szClassname );
  1523. if ( !pEntity )
  1524. {
  1525. Warning( "NULL Ent in UTIL_PrecacheOther\n" );
  1526. return;
  1527. }
  1528. // If we have a specified model, set it before calling precache
  1529. if ( modelName && modelName[0] )
  1530. {
  1531. pEntity->SetModelName( AllocPooledString( modelName ) );
  1532. }
  1533. if (pEntity)
  1534. pEntity->Precache( );
  1535. UTIL_RemoveImmediate( pEntity );
  1536. }
  1537. //=========================================================
  1538. // UTIL_LogPrintf - Prints a logged message to console.
  1539. // Preceded by LOG: ( timestamp ) < message >
  1540. //=========================================================
  1541. void UTIL_LogPrintf( const char *fmt, ... )
  1542. {
  1543. if ( !g_bIsLogging )
  1544. return;
  1545. va_list argptr;
  1546. char tempString[1024];
  1547. va_start ( argptr, fmt );
  1548. Q_vsnprintf( tempString, sizeof(tempString), fmt, argptr );
  1549. va_end ( argptr );
  1550. // Print to server console
  1551. engine->LogPrint( tempString );
  1552. }
  1553. //=========================================================
  1554. // UTIL_DotPoints - returns the dot product of a line from
  1555. // src to check and vecdir.
  1556. //=========================================================
  1557. float UTIL_DotPoints ( const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir )
  1558. {
  1559. Vector2D vec2LOS;
  1560. vec2LOS = ( vecCheck - vecSrc ).AsVector2D();
  1561. Vector2DNormalize( vec2LOS );
  1562. return DotProduct2D(vec2LOS, vecDir.AsVector2D());
  1563. }
  1564. //=========================================================
  1565. // UTIL_StripToken - for redundant keynames
  1566. //=========================================================
  1567. void UTIL_StripToken( const char *pKey, char *pDest )
  1568. {
  1569. int i = 0;
  1570. while ( pKey[i] && pKey[i] != '#' )
  1571. {
  1572. pDest[i] = pKey[i];
  1573. i++;
  1574. }
  1575. pDest[i] = 0;
  1576. }
  1577. // computes gravity scale for an absolute gravity. Pass the result into CBaseEntity::SetGravity()
  1578. float UTIL_ScaleForGravity( float desiredGravity )
  1579. {
  1580. float worldGravity = sv_gravity.GetFloat();
  1581. return worldGravity > 0 ? desiredGravity / worldGravity : 0;
  1582. }
  1583. //-----------------------------------------------------------------------------
  1584. // Purpose: Implemented for mathlib.c error handling
  1585. // Input : *error -
  1586. //-----------------------------------------------------------------------------
  1587. extern "C" void Sys_Error( char *error, ... )
  1588. {
  1589. va_list argptr;
  1590. char string[1024];
  1591. va_start( argptr, error );
  1592. Q_vsnprintf( string, sizeof(string), error, argptr );
  1593. va_end( argptr );
  1594. Warning( "%s", string );
  1595. Assert(0);
  1596. }
  1597. //-----------------------------------------------------------------------------
  1598. // Purpose: Spawns an entity into the game, initializing it with the map ent data block
  1599. // Input : *pEntity - the newly created entity
  1600. // *mapData - pointer a block of entity map data
  1601. // Output : -1 if the entity was not successfully created; 0 on success
  1602. //-----------------------------------------------------------------------------
  1603. int DispatchSpawn( CBaseEntity *pEntity, bool bRunVScripts )
  1604. {
  1605. if ( pEntity )
  1606. {
  1607. MDLCACHE_CRITICAL_SECTION();
  1608. // keep a smart pointer that will now if the object gets deleted
  1609. EHANDLE pEntSafe;
  1610. pEntSafe = pEntity;
  1611. // Initialize these or entities who don't link to the world won't have anything in here
  1612. // is this necessary?
  1613. //pEntity->SetAbsMins( pEntity->GetOrigin() - Vector(1,1,1) );
  1614. //pEntity->SetAbsMaxs( pEntity->GetOrigin() + Vector(1,1,1) );
  1615. if( bRunVScripts )
  1616. {
  1617. pEntity->RunVScripts();
  1618. pEntity->RunPrecacheScripts();
  1619. }
  1620. #if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG)
  1621. const char *pszClassname = NULL;
  1622. int iClassname = ((CEntityFactoryDictionary*)EntityFactoryDictionary())->m_Factories.Find( pEntity->GetClassname() );
  1623. if ( iClassname != ((CEntityFactoryDictionary*)EntityFactoryDictionary())->m_Factories.InvalidIndex() )
  1624. pszClassname = ((CEntityFactoryDictionary*)EntityFactoryDictionary())->m_Factories.GetElementName( iClassname );
  1625. if ( pszClassname )
  1626. {
  1627. MemAlloc_PushAllocDbgInfo( pszClassname, __LINE__ );
  1628. }
  1629. #endif
  1630. bool bAsyncAnims = mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, false );
  1631. CBaseAnimating *pAnimating = pEntity->GetBaseAnimating();
  1632. if (!pAnimating)
  1633. {
  1634. pEntity->Spawn();
  1635. }
  1636. else
  1637. {
  1638. // Don't allow the PVS check to skip animation setup during spawning
  1639. pAnimating->SetBoneCacheFlags( BCF_IS_IN_SPAWN );
  1640. pEntity->Spawn();
  1641. pAnimating->ClearBoneCacheFlags( BCF_IS_IN_SPAWN );
  1642. }
  1643. mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, bAsyncAnims );
  1644. #if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG)
  1645. if ( pszClassname )
  1646. {
  1647. MemAlloc_PopAllocDbgInfo();
  1648. }
  1649. #endif
  1650. // Try to get the pointer again, in case the spawn function deleted the entity.
  1651. // UNDONE: Spawn() should really return a code to ask that the entity be deleted, but
  1652. // that would touch too much code for me to do that right now.
  1653. if ( pEntSafe == NULL || pEntity->IsMarkedForDeletion() )
  1654. return -1;
  1655. if ( pEntity->m_iGlobalname != NULL_STRING )
  1656. {
  1657. // Handle global stuff here
  1658. int globalIndex = GlobalEntity_GetIndex( pEntity->m_iGlobalname );
  1659. if ( globalIndex >= 0 )
  1660. {
  1661. // Already dead? delete
  1662. if ( GlobalEntity_GetState(globalIndex) == GLOBAL_DEAD )
  1663. {
  1664. pEntity->Remove();
  1665. return -1;
  1666. }
  1667. else if ( !FStrEq(STRING(gpGlobals->mapname), GlobalEntity_GetMap(globalIndex)) )
  1668. {
  1669. pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive
  1670. }
  1671. // In this level & not dead, continue on as normal
  1672. }
  1673. else
  1674. {
  1675. // Spawned entities default to 'On'
  1676. GlobalEntity_Add( pEntity->m_iGlobalname, gpGlobals->mapname, GLOBAL_ON );
  1677. // Msg( "Added global entity %s (%s)\n", pEntity->GetClassname(), STRING(pEntity->m_iGlobalname) );
  1678. }
  1679. }
  1680. gEntList.NotifySpawn( pEntity );
  1681. if( bRunVScripts )
  1682. {
  1683. pEntity->RunOnPostSpawnScripts();
  1684. }
  1685. }
  1686. return 0;
  1687. }
  1688. // UNDONE: This could be a better test - can we run the absbox through the bsp and see
  1689. // if it contains any solid space? or would that eliminate some entities we want to keep?
  1690. int UTIL_EntityInSolid( CBaseEntity *ent )
  1691. {
  1692. Vector point;
  1693. CBaseEntity *pParent = ent->GetMoveParent();
  1694. // HACKHACK -- If you're attached to a client, always go through
  1695. if ( pParent )
  1696. {
  1697. if ( pParent->IsPlayer() )
  1698. return 0;
  1699. ent = ent->GetRootMoveParent();
  1700. }
  1701. point = ent->WorldSpaceCenter();
  1702. return ( enginetrace->GetPointContents( point ) & MASK_SOLID );
  1703. }
  1704. //-----------------------------------------------------------------------------
  1705. // Purpose: Initialize the matrix from an entity
  1706. // Input : *pEntity -
  1707. //-----------------------------------------------------------------------------
  1708. void EntityMatrix::InitFromEntity( CBaseEntity *pEntity, int iAttachment )
  1709. {
  1710. if ( !pEntity )
  1711. {
  1712. Identity();
  1713. return;
  1714. }
  1715. // Get an attachment's matrix?
  1716. if ( iAttachment != 0 )
  1717. {
  1718. CBaseAnimating *pAnimating = pEntity->GetBaseAnimating();
  1719. if ( pAnimating && pAnimating->GetModelPtr() )
  1720. {
  1721. Vector vOrigin;
  1722. QAngle vAngles;
  1723. if ( pAnimating->GetAttachment( iAttachment, vOrigin, vAngles ) )
  1724. {
  1725. ((VMatrix *)this)->SetupMatrixOrgAngles( vOrigin, vAngles );
  1726. return;
  1727. }
  1728. }
  1729. }
  1730. ((VMatrix *)this)->SetupMatrixOrgAngles( pEntity->GetAbsOrigin(), pEntity->GetAbsAngles() );
  1731. }
  1732. void EntityMatrix::InitFromEntityLocal( CBaseEntity *entity )
  1733. {
  1734. if ( !entity || !entity->edict() )
  1735. {
  1736. Identity();
  1737. return;
  1738. }
  1739. ((VMatrix *)this)->SetupMatrixOrgAngles( entity->GetLocalOrigin(), entity->GetLocalAngles() );
  1740. }
  1741. //==================================================
  1742. // Purpose:
  1743. // Input:
  1744. // Output:
  1745. //==================================================
  1746. void UTIL_ValidateSoundName( string_t &name, const char *defaultStr )
  1747. {
  1748. if ( ( !name ||
  1749. strlen( (char*) STRING( name ) ) < 1 ) ||
  1750. !Q_stricmp( (char *)STRING(name), "0" ) )
  1751. {
  1752. name = AllocPooledString( defaultStr );
  1753. }
  1754. }
  1755. //-----------------------------------------------------------------------------
  1756. // Purpose: Helper for UTIL_FindClientInPVS
  1757. // Input : check - last checked client
  1758. // Output : static int UTIL_GetNewCheckClient
  1759. //-----------------------------------------------------------------------------
  1760. // FIXME: include bspfile.h here?
  1761. class CCheckClient : public CAutoGameSystem
  1762. {
  1763. public:
  1764. CCheckClient( char const *name ) : CAutoGameSystem( name )
  1765. {
  1766. }
  1767. void LevelInitPreEntity()
  1768. {
  1769. m_checkCluster = -1;
  1770. m_lastcheck = 1;
  1771. m_lastchecktime = -1;
  1772. m_bClientPVSIsExpanded = false;
  1773. }
  1774. byte m_checkPVS[MAX_MAP_LEAFS/8];
  1775. byte m_checkVisibilityPVS[MAX_MAP_LEAFS/8];
  1776. int m_checkCluster;
  1777. int m_lastcheck;
  1778. float m_lastchecktime;
  1779. bool m_bClientPVSIsExpanded;
  1780. };
  1781. CCheckClient g_CheckClient( "CCheckClient" );
  1782. static int UTIL_GetNewCheckClient( int check )
  1783. {
  1784. int i;
  1785. edict_t *ent;
  1786. Vector org;
  1787. // cycle to the next one
  1788. if (check < 1)
  1789. check = 1;
  1790. if (check > gpGlobals->maxClients)
  1791. check = gpGlobals->maxClients;
  1792. if (check == gpGlobals->maxClients)
  1793. i = 1;
  1794. else
  1795. i = check + 1;
  1796. for ( ; ; i++)
  1797. {
  1798. if ( i > gpGlobals->maxClients )
  1799. {
  1800. i = 1;
  1801. }
  1802. ent = INDEXENT( i );
  1803. if ( !ent )
  1804. continue;
  1805. // Looped but didn't find anything else
  1806. if ( i == check )
  1807. break;
  1808. if ( !ent->GetUnknown() )
  1809. continue;
  1810. CBaseEntity *entity = GetContainingEntity( ent );
  1811. if ( !entity )
  1812. continue;
  1813. if ( entity->GetFlags() & FL_NOTARGET )
  1814. continue;
  1815. // anything that is a client, or has a client as an enemy
  1816. break;
  1817. }
  1818. if ( i != check )
  1819. {
  1820. memset( g_CheckClient.m_checkVisibilityPVS, 0, sizeof(g_CheckClient.m_checkVisibilityPVS) );
  1821. g_CheckClient.m_bClientPVSIsExpanded = false;
  1822. }
  1823. if ( ent )
  1824. {
  1825. // get the PVS for the entity
  1826. CBaseEntity *pce = GetContainingEntity( ent );
  1827. if ( !pce )
  1828. return i;
  1829. org = pce->EyePosition();
  1830. int clusterIndex = engine->GetClusterForOrigin( org );
  1831. if ( clusterIndex != g_CheckClient.m_checkCluster )
  1832. {
  1833. g_CheckClient.m_checkCluster = clusterIndex;
  1834. engine->GetPVSForCluster( clusterIndex, sizeof(g_CheckClient.m_checkPVS), g_CheckClient.m_checkPVS );
  1835. #if defined ( PORTAL )
  1836. // Add in any clusters seen by portals.
  1837. int iPortalCount = CPortal_Base2D_Shared::AllPortals.Count();
  1838. if( iPortalCount > 0 )
  1839. {
  1840. CPortal_Base2D **pPortals = CPortal_Base2D_Shared::AllPortals.Base();
  1841. for( int i = 0; i != iPortalCount; ++i )
  1842. {
  1843. CPortal_Base2D *pPortal = pPortals[i];
  1844. if ( pPortal && pPortal->IsActivedAndLinked() )
  1845. {
  1846. // add only portals visible in the new cluster the client check ent just moved into.
  1847. if ( pPortal->NetworkProp()->IsInPVS( ent, g_CheckClient.m_checkPVS , sizeof( g_CheckClient.m_checkPVS ) ) )
  1848. {
  1849. // add what it can see to the check pvs
  1850. AddPortalVisibilityToPVS( pPortal, sizeof( g_CheckClient.m_checkPVS ), g_CheckClient.m_checkPVS );
  1851. }
  1852. }
  1853. }
  1854. }
  1855. #endif // PORTAL
  1856. }
  1857. }
  1858. return i;
  1859. }
  1860. //-----------------------------------------------------------------------------
  1861. // Gets the current check client....
  1862. //-----------------------------------------------------------------------------
  1863. static edict_t *UTIL_GetCurrentCheckClient()
  1864. {
  1865. edict_t *ent;
  1866. // find a new check if on a new frame
  1867. float delta = gpGlobals->curtime - g_CheckClient.m_lastchecktime;
  1868. if ( delta >= 0.1 || delta < 0 )
  1869. {
  1870. g_CheckClient.m_lastcheck = UTIL_GetNewCheckClient( g_CheckClient.m_lastcheck );
  1871. g_CheckClient.m_lastchecktime = gpGlobals->curtime;
  1872. }
  1873. // return check if it might be visible
  1874. ent = INDEXENT( g_CheckClient.m_lastcheck );
  1875. // Allow dead clients -- JAY
  1876. // Our monsters know the difference, and this function gates alot of behavior
  1877. // It's annoying to die and see monsters stop thinking because you're no longer
  1878. // "in" their PVS
  1879. if ( !ent || ent->IsFree() || !ent->GetUnknown())
  1880. {
  1881. return NULL;
  1882. }
  1883. return ent;
  1884. }
  1885. #if defined ( PORTAL )
  1886. void UTIL_SetClientCheckPVS( edict_t *pClient, const unsigned char *pvs, int pvssize )
  1887. {
  1888. Assert( (pClient == UTIL_GetCurrentCheckClient()) || g_pGameRules->IsMultiplayer() ); //double check that the code is nondivergent from existing behavior in single player
  1889. if( pClient == UTIL_GetCurrentCheckClient() )
  1890. {
  1891. // this sets whatever client check is the current g_ClientCheck
  1892. Assert( pvssize <= sizeof(g_CheckClient.m_checkPVS) );
  1893. memcpy( g_CheckClient.m_checkPVS, pvs, pvssize );
  1894. }
  1895. }
  1896. #endif
  1897. void UTIL_SetClientVisibilityPVS( edict_t *pClient, const unsigned char *pvs, int pvssize )
  1898. {
  1899. if ( pClient == UTIL_GetCurrentCheckClient() )
  1900. {
  1901. Assert( pvssize <= sizeof(g_CheckClient.m_checkVisibilityPVS) );
  1902. g_CheckClient.m_bClientPVSIsExpanded = false;
  1903. unsigned *pFrom = (unsigned *)pvs;
  1904. unsigned *pMask = (unsigned *)g_CheckClient.m_checkPVS;
  1905. unsigned *pTo = (unsigned *)g_CheckClient.m_checkVisibilityPVS;
  1906. int limit = pvssize / 4;
  1907. int i;
  1908. for ( i = 0; i < limit; i++ )
  1909. {
  1910. pTo[i] = pFrom[i] & ~pMask[i];
  1911. if ( pFrom[i] )
  1912. {
  1913. g_CheckClient.m_bClientPVSIsExpanded = true;
  1914. }
  1915. }
  1916. int remainder = pvssize % 4;
  1917. for ( i = 0; i < remainder; i++ )
  1918. {
  1919. ((unsigned char *)&pTo[limit])[i] = ((unsigned char *)&pFrom[limit])[i] & !((unsigned char *)&pMask[limit])[i];
  1920. if ( ((unsigned char *)&pFrom[limit])[i] != 0)
  1921. {
  1922. g_CheckClient.m_bClientPVSIsExpanded = true;
  1923. }
  1924. }
  1925. }
  1926. }
  1927. bool UTIL_ClientPVSIsExpanded()
  1928. {
  1929. return g_CheckClient.m_bClientPVSIsExpanded;
  1930. }
  1931. //-----------------------------------------------------------------------------
  1932. // Purpose: Returns a client (or object that has a client enemy) that would be a valid target.
  1933. // If there are more than one valid options, they are cycled each frame
  1934. // If (self.origin + self.viewofs) is not in the PVS of the current target, it is not returned at all.
  1935. // Input : *pEdict -
  1936. // Output : edict_t*
  1937. //-----------------------------------------------------------------------------
  1938. CBaseEntity *UTIL_FindClientInPVS( const Vector &vecBoxMins, const Vector &vecBoxMaxs )
  1939. {
  1940. edict_t *ent = UTIL_GetCurrentCheckClient();
  1941. if ( !ent )
  1942. {
  1943. return NULL;
  1944. }
  1945. if ( !engine->CheckBoxInPVS( vecBoxMins, vecBoxMaxs, g_CheckClient.m_checkPVS, sizeof( g_CheckClient.m_checkPVS ) ) )
  1946. {
  1947. return NULL;
  1948. }
  1949. // might be able to see it
  1950. return GetContainingEntity( ent );
  1951. }
  1952. //-----------------------------------------------------------------------------
  1953. // Purpose: Returns a client (or object that has a client enemy) that would be a valid target.
  1954. // If there are more than one valid options, they are cycled each frame
  1955. // If (self.origin + self.viewofs) is not in the PVS of the current target, it is not returned at all.
  1956. // Input : *pEdict -
  1957. // Output : edict_t*
  1958. //-----------------------------------------------------------------------------
  1959. ConVar sv_strict_notarget( "sv_strict_notarget", "0", 0, "If set, notarget will cause entities to never think they are in the pvs" );
  1960. edict_t *UTIL_FindClientInPVSGuts(edict_t *pEdict, unsigned char *pvs, unsigned pvssize )
  1961. {
  1962. Vector view;
  1963. edict_t *ent = UTIL_GetCurrentCheckClient();
  1964. if ( !ent )
  1965. {
  1966. return NULL;
  1967. }
  1968. CBaseEntity *pPlayerEntity = GetContainingEntity( ent );
  1969. if( (!pPlayerEntity || (pPlayerEntity->GetFlags() & FL_NOTARGET)) && sv_strict_notarget.GetBool() )
  1970. {
  1971. return NULL;
  1972. }
  1973. // if current entity can't possibly see the check entity, return 0
  1974. // UNDONE: Build a box for this and do it over that box
  1975. // UNDONE: Use CM_BoxLeafnums()
  1976. CBaseEntity *pe = GetContainingEntity( pEdict );
  1977. if ( pe )
  1978. {
  1979. view = pe->EyePosition();
  1980. if ( !engine->CheckOriginInPVS( view, pvs, pvssize ) )
  1981. {
  1982. return NULL;
  1983. }
  1984. }
  1985. // might be able to see it
  1986. return ent;
  1987. }
  1988. //-----------------------------------------------------------------------------
  1989. // Purpose: Returns a client that could see the entity directly
  1990. //-----------------------------------------------------------------------------
  1991. edict_t *UTIL_FindClientInPVS(edict_t *pEdict)
  1992. {
  1993. return g_pGameRules->DoFindClientInPVS( pEdict, g_CheckClient.m_checkPVS, sizeof( g_CheckClient.m_checkPVS ) );
  1994. }
  1995. //-----------------------------------------------------------------------------
  1996. // Purpose: Returns a client that could see the entity, including through a camera
  1997. //-----------------------------------------------------------------------------
  1998. edict_t *UTIL_FindClientInVisibilityPVS( edict_t *pEdict )
  1999. {
  2000. return g_pGameRules->DoFindClientInPVS( pEdict, g_CheckClient.m_checkVisibilityPVS, sizeof( g_CheckClient.m_checkVisibilityPVS ) );
  2001. }
  2002. //-----------------------------------------------------------------------------
  2003. // Purpose: Returns a chain of entities within the PVS of another entity (client)
  2004. // starting_ent is the ent currently at in the list
  2005. // a starting_ent of NULL signifies the beginning of a search
  2006. // Input : *pplayer -
  2007. // *starting_ent -
  2008. // Output : edict_t
  2009. //-----------------------------------------------------------------------------
  2010. CBaseEntity *UTIL_EntitiesInPVS( CBaseEntity *pPVSEntity, CBaseEntity *pStartingEntity )
  2011. {
  2012. Vector org;
  2013. static byte pvs[ MAX_MAP_CLUSTERS/8 ];
  2014. static Vector lastOrg( 0, 0, 0 );
  2015. static int lastCluster = -1;
  2016. if ( !pPVSEntity )
  2017. return NULL;
  2018. // NOTE: These used to be caching code here to prevent this from
  2019. // being called over+over which breaks when you go back + forth
  2020. // across level transitions
  2021. // So, we'll always get the PVS each time we start a new EntitiesInPVS iteration.
  2022. // Given that weapon_binocs + leveltransition code is the only current clients
  2023. // of this, this seems safe.
  2024. if ( !pStartingEntity )
  2025. {
  2026. org = pPVSEntity->EyePosition();
  2027. int clusterIndex = engine->GetClusterForOrigin( org );
  2028. Assert( clusterIndex >= 0 );
  2029. engine->GetPVSForCluster( clusterIndex, sizeof(pvs), pvs );
  2030. }
  2031. for ( CBaseEntity *pEntity = gEntList.NextEnt(pStartingEntity); pEntity; pEntity = gEntList.NextEnt(pEntity) )
  2032. {
  2033. // Only return attached ents.
  2034. if ( !pEntity->edict() )
  2035. continue;
  2036. CBaseEntity *pParent = pEntity->GetRootMoveParent();
  2037. Vector vecSurroundMins, vecSurroundMaxs;
  2038. pParent->CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
  2039. if ( !engine->CheckBoxInPVS( vecSurroundMins, vecSurroundMaxs, pvs, sizeof( pvs ) ) )
  2040. continue;
  2041. return pEntity;
  2042. }
  2043. return NULL;
  2044. }
  2045. //-----------------------------------------------------------------------------
  2046. // Purpose: Get the predicted postion of an entity of a certain number of seconds
  2047. // Use this function with caution, it has great potential for annoying the player, especially
  2048. // if used for target firing predition
  2049. // Input : *pTarget - target entity to predict
  2050. // timeDelta - amount of time to predict ahead (in seconds)
  2051. // &vecPredictedPosition - output
  2052. //-----------------------------------------------------------------------------
  2053. void UTIL_PredictedPosition( CBaseEntity *pTarget, float flTimeDelta, Vector *vecPredictedPosition )
  2054. {
  2055. if ( ( pTarget == NULL ) || ( vecPredictedPosition == NULL ) )
  2056. return;
  2057. Vector vecPredictedVel;
  2058. //FIXME: Should we look at groundspeed or velocity for non-clients??
  2059. //Get the proper velocity to predict with
  2060. CBasePlayer *pPlayer = ToBasePlayer( pTarget );
  2061. //Player works differently than other entities
  2062. if ( pPlayer != NULL )
  2063. {
  2064. if ( pPlayer->IsInAVehicle() )
  2065. {
  2066. //Calculate the predicted position in this vehicle
  2067. vecPredictedVel = pPlayer->GetVehicleEntity()->GetSmoothedVelocity();
  2068. }
  2069. else
  2070. {
  2071. //Get the player's stored velocity
  2072. vecPredictedVel = pPlayer->GetSmoothedVelocity();
  2073. }
  2074. }
  2075. else
  2076. {
  2077. // See if we're a combat character in a vehicle
  2078. CBaseCombatCharacter *pCCTarget = pTarget->MyCombatCharacterPointer();
  2079. if ( pCCTarget != NULL && pCCTarget->IsInAVehicle() )
  2080. {
  2081. //Calculate the predicted position in this vehicle
  2082. vecPredictedVel = pCCTarget->GetVehicleEntity()->GetSmoothedVelocity();
  2083. }
  2084. else
  2085. {
  2086. // See if we're an animating entity
  2087. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(pTarget);
  2088. if ( pAnimating != NULL )
  2089. {
  2090. vecPredictedVel = pAnimating->GetGroundSpeedVelocity();
  2091. }
  2092. else
  2093. {
  2094. // Otherwise we're a vanilla entity
  2095. vecPredictedVel = pTarget->GetSmoothedVelocity();
  2096. }
  2097. }
  2098. }
  2099. //Get the result
  2100. (*vecPredictedPosition) = pTarget->GetAbsOrigin() + ( vecPredictedVel * flTimeDelta );
  2101. }
  2102. //-----------------------------------------------------------------------------
  2103. // Purpose: Points the destination entity at the target entity
  2104. // Input : *pDest - entity to be pointed at the target
  2105. // *pTarget - target to point at
  2106. //-----------------------------------------------------------------------------
  2107. bool UTIL_PointAtEntity( CBaseEntity *pDest, CBaseEntity *pTarget )
  2108. {
  2109. if ( ( pDest == NULL ) || ( pTarget == NULL ) )
  2110. {
  2111. return false;
  2112. }
  2113. Vector dir = (pTarget->GetAbsOrigin() - pDest->GetAbsOrigin());
  2114. VectorNormalize( dir );
  2115. //Store off as angles
  2116. QAngle angles;
  2117. VectorAngles( dir, angles );
  2118. pDest->SetLocalAngles( angles );
  2119. pDest->SetAbsAngles( angles );
  2120. return true;
  2121. }
  2122. //-----------------------------------------------------------------------------
  2123. // Purpose: Points the destination entity at the target entity by name
  2124. // Input : *pDest - entity to be pointed at the target
  2125. // strTarget - name of entity to target (will only choose the first!)
  2126. //-----------------------------------------------------------------------------
  2127. void UTIL_PointAtNamedEntity( CBaseEntity *pDest, string_t strTarget )
  2128. {
  2129. //Attempt to find the entity
  2130. if ( !UTIL_PointAtEntity( pDest, gEntList.FindEntityByName( NULL, strTarget ) ) )
  2131. {
  2132. DevMsg( 1, "%s (%s) was unable to point at an entity named: %s\n", pDest->GetClassname(), pDest->GetDebugName(), STRING( strTarget ) );
  2133. }
  2134. }
  2135. //-----------------------------------------------------------------------------
  2136. // Purpose: Copy the pose parameter values from one entity to the other
  2137. // Input : *pSourceEntity - entity to copy from
  2138. // *pDestEntity - entity to copy to
  2139. //-----------------------------------------------------------------------------
  2140. bool UTIL_TransferPoseParameters( CBaseEntity *pSourceEntity, CBaseEntity *pDestEntity )
  2141. {
  2142. CBaseAnimating *pSourceBaseAnimating = dynamic_cast<CBaseAnimating*>( pSourceEntity );
  2143. CBaseAnimating *pDestBaseAnimating = dynamic_cast<CBaseAnimating*>( pDestEntity );
  2144. if ( !pSourceBaseAnimating || !pDestBaseAnimating )
  2145. return false;
  2146. for ( int iPose = 0; iPose < MAXSTUDIOPOSEPARAM; ++iPose )
  2147. {
  2148. pDestBaseAnimating->SetPoseParameter( iPose, pSourceBaseAnimating->GetPoseParameter( iPose ) );
  2149. }
  2150. return true;
  2151. }
  2152. //-----------------------------------------------------------------------------
  2153. // Purpose: Make a muzzle flash appear
  2154. // Input : &origin - position of the muzzle flash
  2155. // &angles - angles of the fire direction
  2156. // scale - scale of the muzzle flash
  2157. // type - type of muzzle flash
  2158. //-----------------------------------------------------------------------------
  2159. void UTIL_MuzzleFlash( const Vector &origin, const QAngle &angles, int scale, int type )
  2160. {
  2161. CPASFilter filter( origin );
  2162. te->MuzzleFlash( filter, 0.0f, origin, angles, scale, type );
  2163. }
  2164. //-----------------------------------------------------------------------------
  2165. // Purpose:
  2166. // Input : vStartPos - start of the line
  2167. // vEndPos - end of the line
  2168. // vPoint - point to find nearest point to on specified line
  2169. // clampEnds - clamps returned points to being on the line segment specified
  2170. // Output : Vector - nearest point on the specified line
  2171. //-----------------------------------------------------------------------------
  2172. Vector UTIL_PointOnLineNearestPoint(const Vector& vStartPos, const Vector& vEndPos, const Vector& vPoint, bool clampEnds )
  2173. {
  2174. Vector vEndToStart = (vEndPos - vStartPos);
  2175. Vector vOrgToStart = (vPoint - vStartPos);
  2176. float fNumerator = DotProduct(vEndToStart,vOrgToStart);
  2177. float fDenominator = vEndToStart.Length() * vOrgToStart.Length();
  2178. float fIntersectDist = vOrgToStart.Length()*(fNumerator/fDenominator);
  2179. float flLineLength = VectorNormalize( vEndToStart );
  2180. if ( clampEnds )
  2181. {
  2182. fIntersectDist = clamp( fIntersectDist, 0.0f, flLineLength );
  2183. }
  2184. Vector vIntersectPos = vStartPos + vEndToStart * fIntersectDist;
  2185. return vIntersectPos;
  2186. }
  2187. //-----------------------------------------------------------------------------
  2188. //-----------------------------------------------------------------------------
  2189. AngularImpulse WorldToLocalRotation( const VMatrix &localToWorld, const Vector &worldAxis, float rotation )
  2190. {
  2191. // fix axes of rotation to match axes of vector
  2192. Vector rot = worldAxis * rotation;
  2193. // since the matrix maps local to world, do a transpose rotation to get world to local
  2194. AngularImpulse ang = localToWorld.VMul3x3Transpose( rot );
  2195. return ang;
  2196. }
  2197. //-----------------------------------------------------------------------------
  2198. // Purpose:
  2199. // Input : *filename -
  2200. // *pLength -
  2201. // Output : byte
  2202. //-----------------------------------------------------------------------------
  2203. byte *UTIL_LoadFileForMe( const char *filename, int *pLength )
  2204. {
  2205. void *buffer = NULL;
  2206. int length = filesystem->ReadFileEx( filename, "GAME", &buffer, true, true );
  2207. if ( pLength )
  2208. {
  2209. *pLength = length;
  2210. }
  2211. return (byte *)buffer;
  2212. }
  2213. //-----------------------------------------------------------------------------
  2214. // Purpose:
  2215. // Input : *buffer -
  2216. //-----------------------------------------------------------------------------
  2217. void UTIL_FreeFile( byte *buffer )
  2218. {
  2219. filesystem->FreeOptimalReadBuffer( buffer );
  2220. }
  2221. //-----------------------------------------------------------------------------
  2222. // Purpose: Determines whether an entity is within a certain angular tolerance to viewer
  2223. // Input : *pEntity - entity which is the "viewer"
  2224. // vecPosition - position to test against
  2225. // flTolerance - tolerance (as dot-product)
  2226. // *pflDot - if not NULL, holds the
  2227. // Output : Returns true on success, false on failure.
  2228. //-----------------------------------------------------------------------------
  2229. bool UTIL_IsFacingWithinTolerance( CBaseEntity *pViewer, const Vector &vecPosition, float flDotTolerance, float *pflDot /*= NULL*/ )
  2230. {
  2231. if ( pflDot )
  2232. {
  2233. *pflDot = 0.0f;
  2234. }
  2235. // Required elements
  2236. if ( pViewer == NULL )
  2237. return false;
  2238. Vector forward;
  2239. pViewer->GetVectors( &forward, NULL, NULL );
  2240. Vector dir = vecPosition - pViewer->GetAbsOrigin();
  2241. VectorNormalize( dir );
  2242. // Larger dot product corresponds to a smaller angle
  2243. float flDot = dir.Dot( forward );
  2244. // Return the result
  2245. if ( pflDot )
  2246. {
  2247. *pflDot = flDot;
  2248. }
  2249. // Within the goal tolerance
  2250. if ( flDot >= flDotTolerance )
  2251. return true;
  2252. return false;
  2253. }
  2254. //-----------------------------------------------------------------------------
  2255. // Purpose: Determines whether an entity is within a certain angular tolerance to viewer
  2256. // Input : *pEntity - entity which is the "viewer"
  2257. // *pTarget - entity to test against
  2258. // flTolerance - tolerance (as dot-product)
  2259. // *pflDot - if not NULL, holds the
  2260. // Output : Returns true on success, false on failure.
  2261. //-----------------------------------------------------------------------------
  2262. bool UTIL_IsFacingWithinTolerance( CBaseEntity *pViewer, CBaseEntity *pTarget, float flDotTolerance, float *pflDot /*= NULL*/ )
  2263. {
  2264. if ( pViewer == NULL || pTarget == NULL )
  2265. return false;
  2266. return UTIL_IsFacingWithinTolerance( pViewer, pTarget->GetAbsOrigin(), flDotTolerance, pflDot );
  2267. }
  2268. //-----------------------------------------------------------------------------
  2269. // Purpose: Fills in color for debug purposes based on a relationship
  2270. // Input : nRelationship - relationship to test
  2271. // *pR, *pG, *pB - colors to fill
  2272. //-----------------------------------------------------------------------------
  2273. void UTIL_GetDebugColorForRelationship( int nRelationship, int &r, int &g, int &b )
  2274. {
  2275. switch ( nRelationship )
  2276. {
  2277. case D_LI:
  2278. r = 0;
  2279. g = 255;
  2280. b = 0;
  2281. break;
  2282. case D_NU:
  2283. r = 0;
  2284. g = 0;
  2285. b = 255;
  2286. break;
  2287. case D_HT:
  2288. r = 255;
  2289. g = 0;
  2290. b = 0;
  2291. break;
  2292. case D_FR:
  2293. r = 255;
  2294. g = 255;
  2295. b = 0;
  2296. break;
  2297. default:
  2298. r = 255;
  2299. g = 255;
  2300. b = 255;
  2301. break;
  2302. }
  2303. }
  2304. void LoadAndSpawnEntities_ParseEntKVBlockHelper( CBaseEntity *pNode, KeyValues *pkvNode )
  2305. {
  2306. KeyValues *pkvNodeData = pkvNode->GetFirstSubKey();
  2307. while ( pkvNodeData )
  2308. {
  2309. // Handle the connections block
  2310. if ( !Q_strcmp(pkvNodeData->GetName(), "connections") )
  2311. {
  2312. LoadAndSpawnEntities_ParseEntKVBlockHelper( pNode, pkvNodeData );
  2313. }
  2314. else
  2315. {
  2316. pNode->KeyValue( pkvNodeData->GetName(), pkvNodeData->GetString() );
  2317. }
  2318. pkvNodeData = pkvNodeData->GetNextKey();
  2319. }
  2320. }
  2321. //-----------------------------------------------------------------------------
  2322. // Purpose: Loads and parses a file and spawns entities defined in it.
  2323. //-----------------------------------------------------------------------------
  2324. bool UTIL_LoadAndSpawnEntitiesFromScript( CUtlVector <CBaseEntity*> &entities, const char *pScriptFile, const char *pBlock, bool bActivate )
  2325. {
  2326. KeyValues *pkvFile = new KeyValues( pBlock );
  2327. if ( pkvFile->LoadFromFile( filesystem, pScriptFile, "GAME" ) )
  2328. {
  2329. // Load each block, and spawn the entities
  2330. KeyValues *pkvNode = pkvFile->GetFirstSubKey();
  2331. while ( pkvNode )
  2332. {
  2333. // Get name
  2334. const char *pNodeName = pkvNode->GetName();
  2335. if ( stricmp( pNodeName, "entity" ) )
  2336. {
  2337. pkvNode = pkvNode->GetNextKey();
  2338. continue;
  2339. }
  2340. KeyValues *pClassname = pkvNode->FindKey( "classname" );
  2341. if ( pClassname )
  2342. {
  2343. // Use the classname instead
  2344. pNodeName = pClassname->GetString();
  2345. }
  2346. // Spawn the entity
  2347. CBaseEntity *pNode = CreateEntityByName( pNodeName );
  2348. if ( pNode )
  2349. {
  2350. LoadAndSpawnEntities_ParseEntKVBlockHelper( pNode, pkvNode );
  2351. DispatchSpawn( pNode );
  2352. entities.AddToTail( pNode );
  2353. }
  2354. else
  2355. {
  2356. Warning( "UTIL_LoadAndSpawnEntitiesFromScript: Failed to spawn entity, type: '%s'\n", pNodeName );
  2357. }
  2358. // Move to next entity
  2359. pkvNode = pkvNode->GetNextKey();
  2360. }
  2361. if ( bActivate == true )
  2362. {
  2363. bool bAsyncAnims = mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, false );
  2364. // Then activate all the entities
  2365. for ( int i = 0; i < entities.Count(); i++ )
  2366. {
  2367. entities[i]->Activate();
  2368. }
  2369. mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, bAsyncAnims );
  2370. }
  2371. }
  2372. else
  2373. return false;
  2374. return true;
  2375. }
  2376. //-----------------------------------------------------------------------------
  2377. // Purpose: Convert a vector an angle from worldspace to the entity's parent's local space
  2378. // Input : *pEntity - Entity whose parent we're concerned with
  2379. //-----------------------------------------------------------------------------
  2380. void UTIL_ParentToWorldSpace( CBaseEntity *pEntity, Vector &vecPosition, QAngle &vecAngles )
  2381. {
  2382. if ( pEntity == NULL )
  2383. return;
  2384. // Construct the entity-to-world matrix
  2385. // Start with making an entity-to-parent matrix
  2386. matrix3x4_t matEntityToParent;
  2387. AngleMatrix( vecAngles, matEntityToParent );
  2388. MatrixSetColumn( vecPosition, 3, matEntityToParent );
  2389. // concatenate with our parent's transform
  2390. matrix3x4_t matScratch, matResult;
  2391. matrix3x4_t matParentToWorld;
  2392. if ( pEntity->GetParent() != NULL )
  2393. {
  2394. matParentToWorld = pEntity->GetParentToWorldTransform( matScratch );
  2395. }
  2396. else
  2397. {
  2398. matParentToWorld = pEntity->EntityToWorldTransform();
  2399. }
  2400. ConcatTransforms( matParentToWorld, matEntityToParent, matResult );
  2401. // pull our absolute position out of the matrix
  2402. MatrixGetColumn( matResult, 3, vecPosition );
  2403. MatrixAngles( matResult, vecAngles );
  2404. }
  2405. //-----------------------------------------------------------------------------
  2406. // Purpose: Convert a vector and quaternion from worldspace to the entity's parent's local space
  2407. // Input : *pEntity - Entity whose parent we're concerned with
  2408. //-----------------------------------------------------------------------------
  2409. void UTIL_ParentToWorldSpace( CBaseEntity *pEntity, Vector &vecPosition, Quaternion &quat )
  2410. {
  2411. if ( pEntity == NULL )
  2412. return;
  2413. QAngle vecAngles;
  2414. QuaternionAngles( quat, vecAngles );
  2415. UTIL_ParentToWorldSpace( pEntity, vecPosition, vecAngles );
  2416. AngleQuaternion( vecAngles, quat );
  2417. }
  2418. //-----------------------------------------------------------------------------
  2419. // Purpose: Convert a vector an angle from worldspace to the entity's parent's local space
  2420. // Input : *pEntity - Entity whose parent we're concerned with
  2421. //-----------------------------------------------------------------------------
  2422. void UTIL_WorldToParentSpace( CBaseEntity *pEntity, Vector &vecPosition, QAngle &vecAngles )
  2423. {
  2424. if ( pEntity == NULL )
  2425. return;
  2426. // Construct the entity-to-world matrix
  2427. // Start with making an entity-to-parent matrix
  2428. matrix3x4_t matEntityToParent;
  2429. AngleMatrix( vecAngles, matEntityToParent );
  2430. MatrixSetColumn( vecPosition, 3, matEntityToParent );
  2431. // concatenate with our parent's transform
  2432. matrix3x4_t matScratch, matResult;
  2433. matrix3x4_t matWorldToParent;
  2434. if ( pEntity->GetParent() != NULL )
  2435. {
  2436. matScratch = pEntity->GetParentToWorldTransform( matScratch );
  2437. }
  2438. else
  2439. {
  2440. matScratch = pEntity->EntityToWorldTransform();
  2441. }
  2442. MatrixInvert( matScratch, matWorldToParent );
  2443. ConcatTransforms( matWorldToParent, matEntityToParent, matResult );
  2444. // pull our absolute position out of the matrix
  2445. MatrixGetColumn( matResult, 3, vecPosition );
  2446. MatrixAngles( matResult, vecAngles );
  2447. }
  2448. //-----------------------------------------------------------------------------
  2449. // Purpose: Convert a vector and quaternion from worldspace to the entity's parent's local space
  2450. // Input : *pEntity - Entity whose parent we're concerned with
  2451. //-----------------------------------------------------------------------------
  2452. void UTIL_WorldToParentSpace( CBaseEntity *pEntity, Vector &vecPosition, Quaternion &quat )
  2453. {
  2454. if ( pEntity == NULL )
  2455. return;
  2456. QAngle vecAngles;
  2457. QuaternionAngles( quat, vecAngles );
  2458. UTIL_WorldToParentSpace( pEntity, vecPosition, vecAngles );
  2459. AngleQuaternion( vecAngles, quat );
  2460. }
  2461. //-----------------------------------------------------------------------------
  2462. // Purpose: Given a vector, clamps the scalar axes to MAX_COORD_FLOAT ranges from worldsize.h
  2463. // Input : *pVecPos -
  2464. //-----------------------------------------------------------------------------
  2465. void UTIL_BoundToWorldSize( Vector *pVecPos )
  2466. {
  2467. Assert( pVecPos );
  2468. for ( int i = 0; i < 3; ++i )
  2469. {
  2470. (*pVecPos)[ i ] = clamp( (*pVecPos)[ i ], MIN_COORD_FLOAT, MAX_COORD_FLOAT );
  2471. }
  2472. }
  2473. //--------------------------------------------------------------------------------------------------------
  2474. /**
  2475. * Return true if ground is fairly level within the given radius around an entity
  2476. * Trace 4 vertical hull-quadrants and test their collisions and ground heights and normals
  2477. */
  2478. bool UTIL_IsGroundLevel( float radius, const Vector &position, float hullHeight, int mask, const CBaseEntity *ignore, bool debugTraces )
  2479. {
  2480. const int subdivisions = 3;
  2481. const int samples = subdivisions * subdivisions;
  2482. // trace down below floor level to detect ledges and overhangs
  2483. trace_t result[ samples ];
  2484. float size = 2.0f * radius / subdivisions;
  2485. int i, j, s = 0;
  2486. float x, y = -radius;
  2487. for( j=0; j<subdivisions; ++j )
  2488. {
  2489. x = -radius;
  2490. for( int i=0; i<subdivisions; ++i )
  2491. {
  2492. UTIL_TraceHull( position + Vector( 0, 0, hullHeight ),
  2493. position + Vector( 0, 0, -100 ),
  2494. Vector( x, y, 0 ),
  2495. Vector( x + size, y + size, 10.0f ),
  2496. mask, ignore, COLLISION_GROUP_PLAYER_MOVEMENT,
  2497. &result[ s ] );
  2498. if ( debugTraces )
  2499. {
  2500. NDebugOverlay::SweptBox( position + Vector( 0, 0, hullHeight ),
  2501. result[ s ].endpos,
  2502. Vector( x, y, 0 ),
  2503. Vector( x + size, y + size, 10.0f ),
  2504. vec3_angle, 255, 255, 0, 255, 5.0f );
  2505. }
  2506. ++s;
  2507. x += size;
  2508. }
  2509. y += size;
  2510. }
  2511. // UTIL_TraceHull( GetPosition() + Vector( 0, 0, body->GetCrouchHullHeight() ), GetPosition() + Vector( 0, 0, -100 ), Vector( -radius, -radius, 0 ), Vector( 0, 0, 10.0f ), MASK_ZOMBIESOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &result[0] );
  2512. // UTIL_TraceHull( GetPosition() + Vector( 0, 0, body->GetCrouchHullHeight() ), GetPosition() + Vector( 0, 0, -100 ), Vector( -radius, 0, 0 ), Vector( 0, radius, 10.0f ), MASK_ZOMBIESOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &result[1] );
  2513. // UTIL_TraceHull( GetPosition() + Vector( 0, 0, body->GetCrouchHullHeight() ), GetPosition() + Vector( 0, 0, -100 ), Vector( 0, 0, 0 ), Vector( radius, radius, 10.0f ), MASK_ZOMBIESOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &result[2] );
  2514. // UTIL_TraceHull( GetPosition() + Vector( 0, 0, body->GetCrouchHullHeight() ), GetPosition() + Vector( 0, 0, -100 ), Vector( 0, -radius, 0 ), Vector( radius, 0, 10.0f ), MASK_ZOMBIESOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &result[3] );
  2515. for( i=0; i<samples; ++i )
  2516. {
  2517. if ( result[i].startsolid )
  2518. {
  2519. return false;
  2520. }
  2521. if ( result[i].DidHit() )
  2522. {
  2523. const float flat = 0.9f;
  2524. if ( result[i].plane.normal.z < flat )
  2525. {
  2526. // too steep to sit here
  2527. return false;
  2528. }
  2529. const float maxZDelta = 10.0f;
  2530. for( j = 0; j<samples; ++j )
  2531. {
  2532. if ( i != j && abs( result[i].endpos.z - result[j].endpos.z ) > maxZDelta )
  2533. {
  2534. return false;
  2535. }
  2536. }
  2537. }
  2538. }
  2539. return true;
  2540. }
  2541. void UTIL_SendClientCommandKVToPlayer( KeyValues *pKV, CBasePlayer *pPlayer )
  2542. {
  2543. KeyValues::AutoDelete autodelete( pKV );
  2544. if ( pPlayer )
  2545. {
  2546. engine->ClientCommandKeyValues( pPlayer->edict(), pKV->MakeCopy() );
  2547. }
  2548. else
  2549. {
  2550. // On server, send to all players
  2551. for (int i = 1; i <= gpGlobals->maxClients; i++ )
  2552. {
  2553. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  2554. if ( pPlayer )
  2555. {
  2556. engine->ClientCommandKeyValues( pPlayer->edict(), pKV->MakeCopy() );
  2557. }
  2558. }
  2559. }
  2560. }
  2561. CEG_NOINLINE void UTIL_RecordAchievementEvent( const char *pszAchievementname, CBasePlayer *pPlayer /*= NULL*/ )
  2562. {
  2563. bool bIsWriteStat = ( strchr( pszAchievementname, '@' ) || strchr( pszAchievementname, '[' ) || strchr( pszAchievementname, '(' ) ); // Achievements cannot have @[( symbols in names - it's a stat
  2564. KeyValues *pKV;
  2565. if ( bIsWriteStat )
  2566. pKV = new KeyValues( "write_stats", pszAchievementname, 1 );
  2567. else
  2568. pKV = new KeyValues( "write_awards", pszAchievementname, 1 );
  2569. UTIL_SendClientCommandKVToPlayer( pKV, pPlayer );
  2570. }
  2571. CEG_PROTECT_FUNCTION( UTIL_RecordAchievementEvent );
  2572. //=============================================================================
  2573. //
  2574. // Tests!
  2575. //
  2576. #define NUM_KDTREE_TESTS 2500
  2577. #define NUM_KDTREE_ENTITY_SIZE 256
  2578. void CC_KDTreeTest( const CCommand &args )
  2579. {
  2580. Msg( "Testing kd-tree entity queries." );
  2581. // Get the testing spot.
  2582. // CBaseEntity *pSpot = gEntList.FindEntityByClassname( NULL, "info_player_start" );
  2583. // Vector vecStart = pSpot->GetAbsOrigin();
  2584. CBasePlayer *pPlayer = static_cast<CBasePlayer*>( UTIL_GetLocalPlayer() );
  2585. Vector vecStart = pPlayer->GetAbsOrigin();
  2586. static Vector *vecTargets = NULL;
  2587. static bool bFirst = true;
  2588. // Generate the targets - rays (1K long).
  2589. if ( bFirst )
  2590. {
  2591. vecTargets = new Vector [NUM_KDTREE_TESTS];
  2592. double flRadius = 0;
  2593. double flTheta = 0;
  2594. double flPhi = 0;
  2595. for ( int i = 0; i < NUM_KDTREE_TESTS; ++i )
  2596. {
  2597. flRadius += NUM_KDTREE_TESTS * 123.123;
  2598. flRadius = fmod( flRadius, 128.0 );
  2599. flRadius = fabs( flRadius );
  2600. flTheta += NUM_KDTREE_TESTS * 76.76;
  2601. flTheta = fmod( flTheta, (double) DEG2RAD( 360 ) );
  2602. flTheta = fabs( flTheta );
  2603. flPhi += NUM_KDTREE_TESTS * 1997.99;
  2604. flPhi = fmod( flPhi, (double) DEG2RAD( 180 ) );
  2605. flPhi = fabs( flPhi );
  2606. float st, ct, sp, cp;
  2607. SinCos( flTheta, &st, &ct );
  2608. SinCos( flPhi, &sp, &cp );
  2609. vecTargets[i].x = flRadius * ct * sp;
  2610. vecTargets[i].y = flRadius * st * sp;
  2611. vecTargets[i].z = flRadius * cp;
  2612. // Make the trace 1024 units long.
  2613. Vector vecDir = vecTargets[i] - vecStart;
  2614. VectorNormalize( vecDir );
  2615. vecTargets[i] = vecStart + vecDir * 1024;
  2616. }
  2617. bFirst = false;
  2618. }
  2619. int nTestType = 0;
  2620. if ( args.ArgC() >= 2 )
  2621. {
  2622. nTestType = atoi( args[ 1 ] );
  2623. }
  2624. vtune( true );
  2625. #ifdef VPROF_ENABLED
  2626. g_VProfCurrentProfile.Resume();
  2627. g_VProfCurrentProfile.Start();
  2628. g_VProfCurrentProfile.Reset();
  2629. g_VProfCurrentProfile.MarkFrame();
  2630. #endif
  2631. switch ( nTestType )
  2632. {
  2633. case 0:
  2634. {
  2635. VPROF( "TraceTotal" );
  2636. trace_t trace;
  2637. for ( int iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest )
  2638. {
  2639. UTIL_TraceLine( vecStart, vecTargets[iTest], MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace );
  2640. }
  2641. break;
  2642. }
  2643. case 1:
  2644. {
  2645. VPROF( "TraceTotal" );
  2646. trace_t trace;
  2647. for ( int iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest )
  2648. {
  2649. UTIL_TraceHull( vecStart, vecTargets[iTest], VEC_HULL_MIN, VEC_HULL_MAX, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &trace );
  2650. }
  2651. break;
  2652. }
  2653. case 2:
  2654. {
  2655. Vector vecMins[NUM_KDTREE_TESTS];
  2656. Vector vecMaxs[NUM_KDTREE_TESTS];
  2657. int iTest;
  2658. for ( iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest )
  2659. {
  2660. vecMins[iTest] = vecStart;
  2661. vecMaxs[iTest] = vecStart;
  2662. for ( int iAxis = 0; iAxis < 3; ++iAxis )
  2663. {
  2664. if ( vecTargets[iTest].x < vecMins[iTest].x ) { vecMins[iTest].x = vecTargets[iTest].x; }
  2665. if ( vecTargets[iTest].y < vecMins[iTest].y ) { vecMins[iTest].y = vecTargets[iTest].y; }
  2666. if ( vecTargets[iTest].z < vecMins[iTest].z ) { vecMins[iTest].z = vecTargets[iTest].z; }
  2667. if ( vecTargets[iTest].x > vecMaxs[iTest].x ) { vecMaxs[iTest].x = vecTargets[iTest].x; }
  2668. if ( vecTargets[iTest].y > vecMaxs[iTest].y ) { vecMaxs[iTest].y = vecTargets[iTest].y; }
  2669. if ( vecTargets[iTest].z > vecMaxs[iTest].z ) { vecMaxs[iTest].z = vecTargets[iTest].z; }
  2670. }
  2671. }
  2672. VPROF( "TraceTotal" );
  2673. int nCount = 0;
  2674. Vector vecDelta;
  2675. trace_t trace;
  2676. CBaseEntity *pList[1024];
  2677. for ( iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest )
  2678. {
  2679. nCount += UTIL_EntitiesInBox( pList, 1024, vecMins[iTest], vecMaxs[iTest], 0 );
  2680. }
  2681. Msg( "Count = %d\n", nCount );
  2682. break;
  2683. }
  2684. case 3:
  2685. {
  2686. Vector vecDelta;
  2687. float flRadius[NUM_KDTREE_TESTS];
  2688. int iTest;
  2689. for ( iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest )
  2690. {
  2691. VectorSubtract( vecTargets[iTest], vecStart, vecDelta );
  2692. flRadius[iTest] = vecDelta.Length() * 0.5f;
  2693. }
  2694. VPROF( "TraceTotal" );
  2695. int nCount = 0;
  2696. trace_t trace;
  2697. CBaseEntity *pList[1024];
  2698. for ( iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest )
  2699. {
  2700. nCount += UTIL_EntitiesInSphere( pList, 1024, vecStart, flRadius[iTest], 0 );
  2701. }
  2702. Msg( "Count = %d\n", nCount );
  2703. break;
  2704. }
  2705. default:
  2706. {
  2707. break;
  2708. }
  2709. }
  2710. #ifdef VPROF_ENABLED
  2711. g_VProfCurrentProfile.MarkFrame();
  2712. g_VProfCurrentProfile.Pause();
  2713. g_VProfCurrentProfile.OutputReport( VPRT_FULL );
  2714. #endif
  2715. vtune( false );
  2716. }
  2717. static ConCommand kdtree_test( "kdtree_test", CC_KDTreeTest, "Tests spatial partition for entities queries.", FCVAR_CHEAT );
  2718. void CC_VoxelTreeView( void )
  2719. {
  2720. Msg( "VoxelTreeView\n" );
  2721. ::partition->RenderAllObjectsInTree( 10.0f );
  2722. }
  2723. static ConCommand voxeltree_view( "voxeltree_view", CC_VoxelTreeView, "View entities in the voxel-tree.", FCVAR_CHEAT );
  2724. void CC_VoxelTreePlayerView( void )
  2725. {
  2726. Msg( "VoxelTreePlayerView\n" );
  2727. CBasePlayer *pPlayer = static_cast<CBasePlayer*>( UTIL_GetLocalPlayer() );
  2728. Vector vecStart = pPlayer->GetAbsOrigin();
  2729. ::partition->RenderObjectsInPlayerLeafs( vecStart - VEC_HULL_MIN, vecStart + VEC_HULL_MAX, 3.0f );
  2730. }
  2731. static ConCommand voxeltree_playerview( "voxeltree_playerview", CC_VoxelTreePlayerView, "View entities in the voxel-tree at the player position.", FCVAR_CHEAT );
  2732. void CC_VoxelTreeBox( const CCommand &args )
  2733. {
  2734. Vector vecMin, vecMax;
  2735. if ( args.ArgC() >= 6 )
  2736. {
  2737. vecMin.x = atof( args[ 1 ] );
  2738. vecMin.y = atof( args[ 2 ] );
  2739. vecMin.z = atof( args[ 3 ] );
  2740. vecMax.x = atof( args[ 4 ] );
  2741. vecMax.y = atof( args[ 5 ] );
  2742. vecMax.z = atof( args[ 6 ] );
  2743. }
  2744. else
  2745. {
  2746. return;
  2747. }
  2748. float flTime = 10.0f;
  2749. Vector vecPoints[8];
  2750. vecPoints[0].Init( vecMin.x, vecMin.y, vecMin.z );
  2751. vecPoints[1].Init( vecMin.x, vecMax.y, vecMin.z );
  2752. vecPoints[2].Init( vecMax.x, vecMax.y, vecMin.z );
  2753. vecPoints[3].Init( vecMax.x, vecMin.y, vecMin.z );
  2754. vecPoints[4].Init( vecMin.x, vecMin.y, vecMax.z );
  2755. vecPoints[5].Init( vecMin.x, vecMax.y, vecMax.z );
  2756. vecPoints[6].Init( vecMax.x, vecMax.y, vecMax.z );
  2757. vecPoints[7].Init( vecMax.x, vecMin.y, vecMax.z );
  2758. debugoverlay->AddLineOverlay( vecPoints[0], vecPoints[1], 255, 0, 0, true, flTime );
  2759. debugoverlay->AddLineOverlay( vecPoints[1], vecPoints[2], 255, 0, 0, true, flTime );
  2760. debugoverlay->AddLineOverlay( vecPoints[2], vecPoints[3], 255, 0, 0, true, flTime );
  2761. debugoverlay->AddLineOverlay( vecPoints[3], vecPoints[0], 255, 0, 0, true, flTime );
  2762. debugoverlay->AddLineOverlay( vecPoints[4], vecPoints[5], 255, 0, 0, true, flTime );
  2763. debugoverlay->AddLineOverlay( vecPoints[5], vecPoints[6], 255, 0, 0, true, flTime );
  2764. debugoverlay->AddLineOverlay( vecPoints[6], vecPoints[7], 255, 0, 0, true, flTime );
  2765. debugoverlay->AddLineOverlay( vecPoints[7], vecPoints[4], 255, 0, 0, true, flTime );
  2766. debugoverlay->AddLineOverlay( vecPoints[0], vecPoints[4], 255, 0, 0, true, flTime );
  2767. debugoverlay->AddLineOverlay( vecPoints[3], vecPoints[7], 255, 0, 0, true, flTime );
  2768. debugoverlay->AddLineOverlay( vecPoints[1], vecPoints[5], 255, 0, 0, true, flTime );
  2769. debugoverlay->AddLineOverlay( vecPoints[2], vecPoints[6], 255, 0, 0, true, flTime );
  2770. Msg( "VoxelTreeBox - (%f %f %f) to (%f %f %f)\n", vecMin.x, vecMin.y, vecMin.z, vecMax.x, vecMax.y, vecMax.z );
  2771. ::partition->RenderObjectsInBox( vecMin, vecMax, flTime );
  2772. }
  2773. static ConCommand voxeltree_box( "voxeltree_box", CC_VoxelTreeBox, "View entities in the voxel-tree inside box <Vector(min), Vector(max)>.", FCVAR_CHEAT );
  2774. void CC_VoxelTreeSphere( const CCommand &args )
  2775. {
  2776. Vector vecCenter;
  2777. float flRadius;
  2778. if ( args.ArgC() >= 4 )
  2779. {
  2780. vecCenter.x = atof( args[ 1 ] );
  2781. vecCenter.y = atof( args[ 2 ] );
  2782. vecCenter.z = atof( args[ 3 ] );
  2783. flRadius = atof( args[ 3 ] );
  2784. }
  2785. else
  2786. {
  2787. return;
  2788. }
  2789. float flTime = 3.0f;
  2790. Vector vecMin, vecMax;
  2791. vecMin.Init( vecCenter.x - flRadius, vecCenter.y - flRadius, vecCenter.z - flRadius );
  2792. vecMax.Init( vecCenter.x + flRadius, vecCenter.y + flRadius, vecCenter.z + flRadius );
  2793. Vector vecPoints[8];
  2794. vecPoints[0].Init( vecMin.x, vecMin.y, vecMin.z );
  2795. vecPoints[1].Init( vecMin.x, vecMax.y, vecMin.z );
  2796. vecPoints[2].Init( vecMax.x, vecMax.y, vecMin.z );
  2797. vecPoints[3].Init( vecMax.x, vecMin.y, vecMin.z );
  2798. vecPoints[4].Init( vecMin.x, vecMin.y, vecMax.z );
  2799. vecPoints[5].Init( vecMin.x, vecMax.y, vecMax.z );
  2800. vecPoints[6].Init( vecMax.x, vecMax.y, vecMax.z );
  2801. vecPoints[7].Init( vecMax.x, vecMin.y, vecMax.z );
  2802. debugoverlay->AddLineOverlay( vecPoints[0], vecPoints[1], 255, 0, 0, true, flTime );
  2803. debugoverlay->AddLineOverlay( vecPoints[1], vecPoints[2], 255, 0, 0, true, flTime );
  2804. debugoverlay->AddLineOverlay( vecPoints[2], vecPoints[3], 255, 0, 0, true, flTime );
  2805. debugoverlay->AddLineOverlay( vecPoints[3], vecPoints[0], 255, 0, 0, true, flTime );
  2806. debugoverlay->AddLineOverlay( vecPoints[4], vecPoints[5], 255, 0, 0, true, flTime );
  2807. debugoverlay->AddLineOverlay( vecPoints[5], vecPoints[6], 255, 0, 0, true, flTime );
  2808. debugoverlay->AddLineOverlay( vecPoints[6], vecPoints[7], 255, 0, 0, true, flTime );
  2809. debugoverlay->AddLineOverlay( vecPoints[7], vecPoints[4], 255, 0, 0, true, flTime );
  2810. debugoverlay->AddLineOverlay( vecPoints[0], vecPoints[4], 255, 0, 0, true, flTime );
  2811. debugoverlay->AddLineOverlay( vecPoints[3], vecPoints[7], 255, 0, 0, true, flTime );
  2812. debugoverlay->AddLineOverlay( vecPoints[1], vecPoints[5], 255, 0, 0, true, flTime );
  2813. debugoverlay->AddLineOverlay( vecPoints[2], vecPoints[6], 255, 0, 0, true, flTime );
  2814. Msg( "VoxelTreeSphere - (%f %f %f), %f\n", vecCenter.x, vecCenter.y, vecCenter.z, flRadius );
  2815. ::partition->RenderObjectsInSphere( vecCenter, flRadius, flTime );
  2816. }
  2817. static ConCommand voxeltree_sphere( "voxeltree_sphere", CC_VoxelTreeSphere, "View entities in the voxel-tree inside sphere <Vector(center), float(radius)>.", FCVAR_CHEAT );
  2818. #define NUM_COLLISION_TESTS 2500
  2819. void CC_CollisionTest( const CCommand &args )
  2820. {
  2821. if ( !physenv )
  2822. return;
  2823. Msg( "Testing collision system\n" );
  2824. ::partition->ReportStats( "" );
  2825. int i;
  2826. CBaseEntity *pSpot = gEntList.FindEntityByClassname( NULL, "info_player_start");
  2827. Vector start = pSpot->GetAbsOrigin();
  2828. static Vector *targets = NULL;
  2829. static bool first = true;
  2830. static float test[2] = {1,1};
  2831. if ( first )
  2832. {
  2833. targets = new Vector[NUM_COLLISION_TESTS];
  2834. float radius = 0;
  2835. float theta = 0;
  2836. float phi = 0;
  2837. for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
  2838. {
  2839. radius += NUM_COLLISION_TESTS * 123.123;
  2840. radius = fabs(fmod(radius, 128));
  2841. theta += NUM_COLLISION_TESTS * 76.76;
  2842. theta = fabs(fmod(theta, DEG2RAD(360)));
  2843. phi += NUM_COLLISION_TESTS * 1997.99;
  2844. phi = fabs(fmod(phi, DEG2RAD(180)));
  2845. float st, ct, sp, cp;
  2846. SinCos( theta, &st, &ct );
  2847. SinCos( phi, &sp, &cp );
  2848. targets[i].x = radius * ct * sp;
  2849. targets[i].y = radius * st * sp;
  2850. targets[i].z = radius * cp;
  2851. // make the trace 1024 units long
  2852. Vector dir = targets[i] - start;
  2853. VectorNormalize(dir);
  2854. targets[i] = start + dir * 1024;
  2855. }
  2856. first = false;
  2857. }
  2858. //Vector results[NUM_COLLISION_TESTS];
  2859. int testType = 0;
  2860. if ( args.ArgC() >= 2 )
  2861. {
  2862. testType = atoi(args[1]);
  2863. }
  2864. float duration = 0;
  2865. Vector size[2];
  2866. size[0].Init(0,0,0);
  2867. size[1].Init(16,16,16);
  2868. unsigned int dots = 0;
  2869. int nMask = MASK_ALL & ~(CONTENTS_MONSTER | CONTENTS_HITBOX );
  2870. for ( int j = 0; j < 2; j++ )
  2871. {
  2872. float startTime = Plat_FloatTime();
  2873. if ( testType == 1 )
  2874. {
  2875. trace_t tr;
  2876. for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
  2877. {
  2878. UTIL_TraceHull( start, targets[i], -size[1], size[1], nMask, NULL, COLLISION_GROUP_NONE, &tr );
  2879. }
  2880. }
  2881. else
  2882. {
  2883. testType = 0;
  2884. trace_t tr;
  2885. for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
  2886. {
  2887. if ( i == 0 )
  2888. {
  2889. ::partition->RenderLeafsForRayTraceStart( 10.0f );
  2890. }
  2891. UTIL_TraceLine( start, targets[i], nMask, NULL, COLLISION_GROUP_NONE, &tr );
  2892. if ( i == 0 )
  2893. {
  2894. ::partition->RenderLeafsForRayTraceEnd( );
  2895. }
  2896. }
  2897. }
  2898. duration += Plat_FloatTime() - startTime;
  2899. }
  2900. test[testType] = duration;
  2901. Msg("%d collisions in %.2f ms (%u dots)\n", NUM_COLLISION_TESTS, duration*1000, dots );
  2902. ::partition->ReportStats( "" );
  2903. #if 1
  2904. int red = 255, green = 0, blue = 0;
  2905. for ( i = 0; i < 1 /*NUM_COLLISION_TESTS*/; i++ )
  2906. {
  2907. NDebugOverlay::Line( start, targets[i], red, green, blue, false, 2 );
  2908. }
  2909. #endif
  2910. }
  2911. void UTIL_EnsureInstanceBaseline( const char *szClassname )
  2912. {
  2913. CBaseEntity *pEnt = CreateEntityByName( szClassname );
  2914. if ( pEnt )
  2915. {
  2916. engine->EnsureInstanceBaseline( pEnt->entindex() );
  2917. UTIL_RemoveImmediate( pEnt );
  2918. }
  2919. }
  2920. static ConCommand collision_test("collision_test", CC_CollisionTest, "Tests collision system", FCVAR_CHEAT );