Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1317 lines
35 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $NoKeywords: $
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include <stdarg.h>
  10. #include "hud.h"
  11. #include "itextmessage.h"
  12. #include "materialsystem/imaterial.h"
  13. #include "materialsystem/itexture.h"
  14. #include "materialsystem/imaterialsystem.h"
  15. #include "imovehelper.h"
  16. #include "checksum_crc.h"
  17. #include "decals.h"
  18. #include "iefx.h"
  19. #include "view_scene.h"
  20. #include "filesystem.h"
  21. #include "model_types.h"
  22. #include "engine/IEngineTrace.h"
  23. #include "engine/ivmodelinfo.h"
  24. #include "c_te_effect_dispatch.h"
  25. #include <vgui_controls/Controls.h>
  26. #include <vgui/ISurface.h>
  27. #include <vgui/ILocalize.h>
  28. #include "view.h"
  29. #include "ixboxsystem.h"
  30. #include "inputsystem/iinputsystem.h"
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. ConVar localplayer_visionflags( "localplayer_visionflags", "0", FCVAR_DEVELOPMENTONLY );
  34. //-----------------------------------------------------------------------------
  35. // ConVars
  36. //-----------------------------------------------------------------------------
  37. #ifdef _DEBUG
  38. ConVar r_FadeProps( "r_FadeProps", "1" );
  39. #endif
  40. bool g_MakingDevShots = false;
  41. extern ConVar cl_leveloverview;
  42. //-----------------------------------------------------------------------------
  43. // Purpose: Performs a var args printf into a static return buffer
  44. // Input : *format -
  45. // ... -
  46. // Output : char
  47. //-----------------------------------------------------------------------------
  48. char *VarArgs( const char *format, ... )
  49. {
  50. va_list argptr;
  51. static char string[1024];
  52. va_start (argptr, format);
  53. Q_vsnprintf (string, sizeof( string ), format,argptr);
  54. va_end (argptr);
  55. return string;
  56. }
  57. //-----------------------------------------------------------------------------
  58. // Purpose: Returns true if the entity index corresponds to a player slot
  59. // Input : index -
  60. // Output : bool
  61. //-----------------------------------------------------------------------------
  62. bool IsPlayerIndex( int index )
  63. {
  64. return ( index >= 1 && index <= gpGlobals->maxClients ) ? true : false;
  65. }
  66. int GetLocalPlayerIndex( void )
  67. {
  68. C_BasePlayer * player = C_BasePlayer::GetLocalPlayer();
  69. if ( player )
  70. return player->entindex();
  71. else
  72. return 0; // game not started yet
  73. }
  74. // NOTE: cache these because this gets executed hundreds of times per frame
  75. static int g_nLocalPlayerVisionFlagsWeaponsCheck = 0;
  76. static int g_nLocalPlayerVisionFlags = 0;
  77. int GetLocalPlayerVisionFilterFlags( bool bWeaponsCheck /*= false */ )
  78. {
  79. return bWeaponsCheck ? g_nLocalPlayerVisionFlagsWeaponsCheck : g_nLocalPlayerVisionFlags;
  80. }
  81. void UpdateLocalPlayerVisionFlags()
  82. {
  83. g_nLocalPlayerVisionFlagsWeaponsCheck = 0;
  84. g_nLocalPlayerVisionFlags = 0;
  85. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  86. if ( pPlayer )
  87. {
  88. g_nLocalPlayerVisionFlagsWeaponsCheck = pPlayer->GetVisionFilterFlags( true );
  89. g_nLocalPlayerVisionFlags = pPlayer->GetVisionFilterFlags( false );
  90. }
  91. }
  92. bool IsLocalPlayerUsingVisionFilterFlags( int nFlags, bool bWeaponsCheck /* = false */ )
  93. {
  94. int nLocalPlayerFlags = GetLocalPlayerVisionFilterFlags( bWeaponsCheck );
  95. if ( !bWeaponsCheck )
  96. {
  97. // We can only modify the RJ flags with normal checks that won't take the forced kill cam flags that can happen in weapon checks
  98. int nRJShaderFlags = nLocalPlayerFlags;
  99. if ( nRJShaderFlags != 0 && GameRules() && !GameRules()->AllowMapVisionFilterShaders() )
  100. {
  101. nRJShaderFlags = 0;
  102. }
  103. if ( nRJShaderFlags != localplayer_visionflags.GetInt() )
  104. {
  105. localplayer_visionflags.SetValue( nRJShaderFlags );
  106. }
  107. }
  108. return ( nLocalPlayerFlags & nFlags ) == nFlags;
  109. }
  110. bool IsLocalPlayerSpectator( void )
  111. {
  112. C_BasePlayer * player = C_BasePlayer::GetLocalPlayer();
  113. if ( player )
  114. return player->IsObserver();
  115. else
  116. return false; // game not started yet
  117. }
  118. int GetSpectatorMode( void )
  119. {
  120. C_BasePlayer * player = C_BasePlayer::GetLocalPlayer();
  121. if ( player )
  122. return player->GetObserverMode();
  123. else
  124. return OBS_MODE_NONE; // game not started yet
  125. }
  126. int GetSpectatorTarget( void )
  127. {
  128. C_BasePlayer * player = C_BasePlayer::GetLocalPlayer();
  129. if ( player )
  130. {
  131. CBaseEntity * target = player->GetObserverTarget();
  132. if ( target )
  133. return target->entindex();
  134. else
  135. return 0;
  136. }
  137. else
  138. {
  139. return 0; // game not started yet
  140. }
  141. }
  142. int GetLocalPlayerTeam( void )
  143. {
  144. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  145. if ( pPlayer )
  146. return pPlayer->GetTeamNumber();
  147. else
  148. return TEAM_UNASSIGNED;
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Purpose: Convert angles to -180 t 180 range
  152. // Input : angles -
  153. //-----------------------------------------------------------------------------
  154. void NormalizeAngles( QAngle& angles )
  155. {
  156. int i;
  157. // Normalize angles to -180 to 180 range
  158. for ( i = 0; i < 3; i++ )
  159. {
  160. if ( angles[i] > 180.0 )
  161. {
  162. angles[i] -= 360.0;
  163. }
  164. else if ( angles[i] < -180.0 )
  165. {
  166. angles[i] += 360.0;
  167. }
  168. }
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose: Interpolate Euler angles using quaternions to avoid singularities
  172. // Input : start -
  173. // end -
  174. // output -
  175. // frac -
  176. //-----------------------------------------------------------------------------
  177. void InterpolateAngles( const QAngle& start, const QAngle& end, QAngle& output, float frac )
  178. {
  179. Quaternion src, dest;
  180. // Convert to quaternions
  181. AngleQuaternion( start, src );
  182. AngleQuaternion( end, dest );
  183. Quaternion result;
  184. // Slerp
  185. QuaternionSlerp( src, dest, frac, result );
  186. // Convert to euler
  187. QuaternionAngles( result, output );
  188. }
  189. //-----------------------------------------------------------------------------
  190. // Purpose: Simple linear interpolation
  191. // Input : frac -
  192. // src -
  193. // dest -
  194. // output -
  195. //-----------------------------------------------------------------------------
  196. void InterpolateVector( float frac, const Vector& src, const Vector& dest, Vector& output )
  197. {
  198. int i;
  199. for ( i = 0; i < 3; i++ )
  200. {
  201. output[ i ] = src[ i ] + frac * ( dest[ i ] - src[ i ] );
  202. }
  203. }
  204. client_textmessage_t *TextMessageGet( const char *pName )
  205. {
  206. return engine->TextMessageGet( pName );
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Purpose: ScreenHeight returns the height of the screen, in pixels
  210. // Output : int
  211. //-----------------------------------------------------------------------------
  212. int ScreenHeight( void )
  213. {
  214. int w, h;
  215. GetHudSize( w, h );
  216. return h;
  217. }
  218. //-----------------------------------------------------------------------------
  219. // Purpose: ScreenWidth returns the width of the screen, in pixels
  220. // Output : int
  221. //-----------------------------------------------------------------------------
  222. int ScreenWidth( void )
  223. {
  224. int w, h;
  225. GetHudSize( w, h );
  226. return w;
  227. }
  228. //-----------------------------------------------------------------------------
  229. // Purpose: Return the difference between two angles
  230. // Input : destAngle -
  231. // srcAngle -
  232. // Output : float
  233. //-----------------------------------------------------------------------------
  234. float UTIL_AngleDiff( float destAngle, float srcAngle )
  235. {
  236. float delta;
  237. delta = destAngle - srcAngle;
  238. if ( destAngle > srcAngle )
  239. {
  240. while ( delta >= 180 )
  241. delta -= 360;
  242. }
  243. else
  244. {
  245. while ( delta <= -180 )
  246. delta += 360;
  247. }
  248. return delta;
  249. }
  250. float UTIL_WaterLevel( const Vector &position, float minz, float maxz )
  251. {
  252. Vector midUp = position;
  253. midUp.z = minz;
  254. if ( !(UTIL_PointContents(midUp) & MASK_WATER) )
  255. return minz;
  256. midUp.z = maxz;
  257. if ( UTIL_PointContents(midUp) & MASK_WATER )
  258. return maxz;
  259. float diff = maxz - minz;
  260. while (diff > 1.0)
  261. {
  262. midUp.z = minz + diff/2.0;
  263. if ( UTIL_PointContents(midUp) & MASK_WATER )
  264. {
  265. minz = midUp.z;
  266. }
  267. else
  268. {
  269. maxz = midUp.z;
  270. }
  271. diff = maxz - minz;
  272. }
  273. return midUp.z;
  274. }
  275. void UTIL_Bubbles( const Vector& mins, const Vector& maxs, int count )
  276. {
  277. Vector mid = (mins + maxs) * 0.5;
  278. float flHeight = UTIL_WaterLevel( mid, mid.z, mid.z + 1024 );
  279. flHeight = flHeight - mins.z;
  280. CPASFilter filter( mid );
  281. int bubbles = modelinfo->GetModelIndex( "sprites/bubble.vmt" );
  282. te->Bubbles( filter, 0.0,
  283. &mins, &maxs, flHeight, bubbles, count, 8.0 );
  284. }
  285. void UTIL_ScreenShake( const Vector &center, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake )
  286. {
  287. // Nothing for now
  288. }
  289. char TEXTURETYPE_Find( trace_t *ptr )
  290. {
  291. surfacedata_t *psurfaceData = physprops->GetSurfaceData( ptr->surface.surfaceProps );
  292. return psurfaceData->game.material;
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Purpose: Make a tracer effect
  296. //-----------------------------------------------------------------------------
  297. void UTIL_Tracer( const Vector &vecStart, const Vector &vecEnd, int iEntIndex, int iAttachment, float flVelocity, bool bWhiz, char *pCustomTracerName )
  298. {
  299. CEffectData data;
  300. data.m_vStart = vecStart;
  301. data.m_vOrigin = vecEnd;
  302. data.m_hEntity = ClientEntityList().EntIndexToHandle( iEntIndex );
  303. data.m_flScale = flVelocity;
  304. // Flags
  305. if ( bWhiz )
  306. {
  307. data.m_fFlags |= TRACER_FLAG_WHIZ;
  308. }
  309. if ( iAttachment != TRACER_DONT_USE_ATTACHMENT )
  310. {
  311. data.m_fFlags |= TRACER_FLAG_USEATTACHMENT;
  312. // Stomp the start, since it's not going to be used anyway
  313. data.m_vStart[0] = iAttachment;
  314. }
  315. // Fire it off
  316. if ( pCustomTracerName )
  317. {
  318. DispatchEffect( pCustomTracerName, data );
  319. }
  320. else
  321. {
  322. DispatchEffect( "Tracer", data );
  323. }
  324. }
  325. //------------------------------------------------------------------------------
  326. // Purpose : Creates both an decal and any associated impact effects (such
  327. // as flecks) for the given iDamageType and the trace's end position
  328. // Input :
  329. // Output :
  330. //------------------------------------------------------------------------------
  331. void UTIL_ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
  332. {
  333. C_BaseEntity *pEntity = pTrace->m_pEnt;
  334. // Is the entity valid, is the surface sky?
  335. if ( !pEntity || (pTrace->surface.flags & SURF_SKY) )
  336. return;
  337. if (pTrace->fraction == 1.0)
  338. return;
  339. // don't decal nodraw surfaces
  340. if ( pTrace->surface.flags & SURF_NODRAW )
  341. return;
  342. pEntity->ImpactTrace( pTrace, iDamageType, pCustomImpactName );
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Purpose:
  346. //-----------------------------------------------------------------------------
  347. int UTIL_PrecacheDecal( const char *name, bool preload )
  348. {
  349. return effects->Draw_DecalIndexFromName( (char*)name );
  350. }
  351. extern short g_sModelIndexSmoke;
  352. void UTIL_Smoke( const Vector &origin, const float scale, const float framerate )
  353. {
  354. CPVSFilter filter( origin );
  355. te->Smoke( filter, 0.0f, &origin, g_sModelIndexSmoke, scale, framerate );
  356. }
  357. void UTIL_SetOrigin( C_BaseEntity *entity, const Vector &vecOrigin )
  358. {
  359. entity->SetLocalOrigin( vecOrigin );
  360. }
  361. //#define PRECACHE_OTHER_ONCE
  362. // UNDONE: Do we need this to avoid doing too much of this? Measure startup times and see
  363. #if PRECACHE_OTHER_ONCE
  364. #include "utlsymbol.h"
  365. class CPrecacheOtherList : public CAutoServerSystem
  366. {
  367. public:
  368. virtual void LevelInitPreEntity();
  369. virtual void LevelShutdownPostEntity();
  370. bool AddOrMarkPrecached( const char *pClassname );
  371. private:
  372. CUtlSymbolTable m_list;
  373. };
  374. void CPrecacheOtherList::LevelInitPreEntity()
  375. {
  376. m_list.RemoveAll();
  377. }
  378. void CPrecacheOtherList::LevelShutdownPostEntity()
  379. {
  380. m_list.RemoveAll();
  381. }
  382. //-----------------------------------------------------------------------------
  383. // Purpose: mark or add
  384. // Input : *pEntity -
  385. // Output : Returns true on success, false on failure.
  386. //-----------------------------------------------------------------------------
  387. bool CPrecacheOtherList::AddOrMarkPrecached( const char *pClassname )
  388. {
  389. CUtlSymbol sym = m_list.Find( pClassname );
  390. if ( sym.IsValid() )
  391. return false;
  392. m_list.AddString( pClassname );
  393. return true;
  394. }
  395. CPrecacheOtherList g_PrecacheOtherList;
  396. #endif
  397. void UTIL_PrecacheOther( const char *szClassname )
  398. {
  399. #if PRECACHE_OTHER_ONCE
  400. // already done this one?, if not, mark as done
  401. if ( !g_PrecacheOtherList.AddOrMarkPrecached( szClassname ) )
  402. return;
  403. #endif
  404. // Client should only do this once entities are coming down from server!!!
  405. // Assert( engine->IsConnected() );
  406. C_BaseEntity *pEntity = CreateEntityByName( szClassname );
  407. if ( !pEntity )
  408. {
  409. Warning( "NULL Ent in UTIL_PrecacheOther\n" );
  410. return;
  411. }
  412. if (pEntity)
  413. {
  414. pEntity->Precache( );
  415. }
  416. // Bye bye
  417. pEntity->Release();
  418. }
  419. static csurface_t g_NullSurface = { "**empty**", 0 };
  420. //-----------------------------------------------------------------------------
  421. // Purpose:
  422. //-----------------------------------------------------------------------------
  423. void UTIL_SetTrace(trace_t& trace, const Ray_t& ray, C_BaseEntity *ent, float fraction, int hitgroup, unsigned int contents, const Vector& normal, float intercept )
  424. {
  425. trace.startsolid = (fraction == 0.0f);
  426. trace.fraction = fraction;
  427. VectorCopy( ray.m_Start, trace.startpos );
  428. VectorMA( ray.m_Start, fraction, ray.m_Delta, trace.endpos );
  429. VectorCopy( normal, trace.plane.normal );
  430. trace.plane.dist = intercept;
  431. trace.m_pEnt = C_BaseEntity::Instance( ent );
  432. trace.hitgroup = hitgroup;
  433. trace.surface = g_NullSurface;
  434. trace.contents = contents;
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose: Get the x & y positions of a world position in screenspace
  438. // Returns true if it's onscreen
  439. //-----------------------------------------------------------------------------
  440. bool GetVectorInScreenSpace( Vector pos, int& iX, int& iY, Vector *vecOffset )
  441. {
  442. Vector screen;
  443. // Apply the offset, if one was specified
  444. if ( vecOffset != NULL )
  445. pos += *vecOffset;
  446. // Transform to screen space
  447. int iFacing = ScreenTransform( pos, screen );
  448. iX = 0.5f * ( 1.0f + screen[0] ) * ScreenWidth();
  449. iY = 0.5f * ( 1.0f - screen[1] ) * ScreenHeight();
  450. // Make sure the player's facing it
  451. if ( iFacing )
  452. {
  453. // We're actually facing away from the Target. Stomp the screen position.
  454. iX = -640;
  455. iY = -640;
  456. return false;
  457. }
  458. return true;
  459. }
  460. //-----------------------------------------------------------------------------
  461. // Purpose: Get the x & y positions of a world position in HUD space
  462. // Returns true if it's onscreen
  463. //-----------------------------------------------------------------------------
  464. bool GetVectorInHudSpace( Vector pos, int& iX, int& iY, Vector *vecOffset )
  465. {
  466. Vector screen;
  467. // Apply the offset, if one was specified
  468. if ( vecOffset != NULL )
  469. pos += *vecOffset;
  470. // Transform to HUD space
  471. int iFacing = HudTransform( pos, screen );
  472. iX = 0.5f * ( 1.0f + screen[0] ) * ScreenWidth();
  473. iY = 0.5f * ( 1.0f - screen[1] ) * ScreenHeight();
  474. // Make sure the player's facing it
  475. if ( iFacing )
  476. {
  477. // We're actually facing away from the Target. Stomp the screen position.
  478. iX = -640;
  479. iY = -640;
  480. return false;
  481. }
  482. return true;
  483. }
  484. //-----------------------------------------------------------------------------
  485. // Purpose: Get the x & y positions of an entity in screenspace
  486. // Returns true if it's onscreen
  487. //-----------------------------------------------------------------------------
  488. bool GetTargetInScreenSpace( C_BaseEntity *pTargetEntity, int& iX, int& iY, Vector *vecOffset )
  489. {
  490. return GetVectorInScreenSpace( pTargetEntity->WorldSpaceCenter(), iX, iY, vecOffset );
  491. }
  492. //-----------------------------------------------------------------------------
  493. // Purpose: Get the x & y positions of an entity in Vgui space
  494. // Returns true if it's onscreen
  495. //-----------------------------------------------------------------------------
  496. bool GetTargetInHudSpace( C_BaseEntity *pTargetEntity, int& iX, int& iY, Vector *vecOffset )
  497. {
  498. return GetVectorInHudSpace( pTargetEntity->WorldSpaceCenter(), iX, iY, vecOffset );
  499. }
  500. //-----------------------------------------------------------------------------
  501. // Purpose:
  502. // Input : *player -
  503. // msg_dest -
  504. // *msg_name -
  505. // *param1 -
  506. // *param2 -
  507. // *param3 -
  508. // *param4 -
  509. //-----------------------------------------------------------------------------
  510. void ClientPrint( C_BasePlayer *player, int msg_dest, const char *msg_name, const char *param1 /*= NULL*/, const char *param2 /*= NULL*/, const char *param3 /*= NULL*/, const char *param4 /*= NULL*/ )
  511. {
  512. }
  513. //-----------------------------------------------------------------------------
  514. // class CFlaggedEntitiesEnum
  515. //-----------------------------------------------------------------------------
  516. // enumerate entities that match a set of edict flags into a static array
  517. class CFlaggedEntitiesEnum : public IPartitionEnumerator
  518. {
  519. public:
  520. CFlaggedEntitiesEnum( C_BaseEntity **pList, int listMax, int flagMask );
  521. // This gets called by the enumeration methods with each element
  522. // that passes the test.
  523. virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity );
  524. int GetCount() { return m_count; }
  525. bool AddToList( C_BaseEntity *pEntity );
  526. private:
  527. C_BaseEntity **m_pList;
  528. int m_listMax;
  529. int m_flagMask;
  530. int m_count;
  531. };
  532. CFlaggedEntitiesEnum::CFlaggedEntitiesEnum( C_BaseEntity **pList, int listMax, int flagMask )
  533. {
  534. m_pList = pList;
  535. m_listMax = listMax;
  536. m_flagMask = flagMask;
  537. m_count = 0;
  538. }
  539. bool CFlaggedEntitiesEnum::AddToList( C_BaseEntity *pEntity )
  540. {
  541. if ( m_count >= m_listMax )
  542. return false;
  543. m_pList[m_count] = pEntity;
  544. m_count++;
  545. return true;
  546. }
  547. IterationRetval_t CFlaggedEntitiesEnum::EnumElement( IHandleEntity *pHandleEntity )
  548. {
  549. IClientEntity *pClientEntity = cl_entitylist->GetClientEntityFromHandle( pHandleEntity->GetRefEHandle() );
  550. C_BaseEntity *pEntity = pClientEntity ? pClientEntity->GetBaseEntity() : NULL;
  551. if ( pEntity )
  552. {
  553. if ( m_flagMask && !(pEntity->GetFlags() & m_flagMask) ) // Does it meet the criteria?
  554. return ITERATION_CONTINUE;
  555. if ( !AddToList( pEntity ) )
  556. return ITERATION_STOP;
  557. }
  558. return ITERATION_CONTINUE;
  559. }
  560. //-----------------------------------------------------------------------------
  561. // Purpose: Pass in an array of pointers and an array size, it fills the array and returns the number inserted
  562. // Input : **pList -
  563. // listMax -
  564. // &mins -
  565. // &maxs -
  566. // flagMask -
  567. // Output : int
  568. //-----------------------------------------------------------------------------
  569. int UTIL_EntitiesInBox( C_BaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask, int partitionMask )
  570. {
  571. CFlaggedEntitiesEnum boxEnum( pList, listMax, flagMask );
  572. ::partition->EnumerateElementsInBox( partitionMask, mins, maxs, false, &boxEnum );
  573. return boxEnum.GetCount();
  574. }
  575. //-----------------------------------------------------------------------------
  576. // Purpose: Pass in an array of pointers and an array size, it fills the array and returns the number inserted
  577. // Input : **pList -
  578. // listMax -
  579. // &center -
  580. // radius -
  581. // flagMask -
  582. // Output : int
  583. //-----------------------------------------------------------------------------
  584. int UTIL_EntitiesInSphere( C_BaseEntity **pList, int listMax, const Vector &center, float radius, int flagMask, int partitionMask )
  585. {
  586. CFlaggedEntitiesEnum sphereEnum( pList, listMax, flagMask );
  587. ::partition->EnumerateElementsInSphere( partitionMask, center, radius, false, &sphereEnum );
  588. return sphereEnum.GetCount();
  589. }
  590. //-----------------------------------------------------------------------------
  591. // Purpose: Pass in an array of pointers and an array size, it fills the array and returns the number inserted
  592. // Input : **pList -
  593. // listMax -
  594. // &ray -
  595. // flagMask -
  596. // Output : int
  597. //-----------------------------------------------------------------------------
  598. int UTIL_EntitiesAlongRay( C_BaseEntity **pList, int listMax, const Ray_t &ray, int flagMask, int partitionMask )
  599. {
  600. CFlaggedEntitiesEnum rayEnum( pList, listMax, flagMask );
  601. ::partition->EnumerateElementsAlongRay( partitionMask, ray, false, &rayEnum );
  602. return rayEnum.GetCount();
  603. }
  604. CEntitySphereQuery::CEntitySphereQuery( const Vector &center, float radius, int flagMask, int partitionMask )
  605. {
  606. m_listIndex = 0;
  607. m_listCount = UTIL_EntitiesInSphere( m_pList, ARRAYSIZE(m_pList), center, radius, flagMask, partitionMask );
  608. }
  609. CBaseEntity *CEntitySphereQuery::GetCurrentEntity()
  610. {
  611. if ( m_listIndex < m_listCount )
  612. return m_pList[m_listIndex];
  613. return NULL;
  614. }
  615. //-----------------------------------------------------------------------------
  616. // Purpose:
  617. // Input : font -
  618. // *str -
  619. // Output : int
  620. //-----------------------------------------------------------------------------
  621. int UTIL_ComputeStringWidth( vgui::HFont& font, const char *str )
  622. {
  623. float pixels = 0;
  624. const char *p = str;
  625. const char *pAfter = p + 1;
  626. const char *pBefore = "\0";
  627. while ( *p )
  628. {
  629. #if USE_GETKERNEDCHARWIDTH
  630. float wide, abcA;
  631. vgui::surface()->GetKernedCharWidth( font, *p, *pBefore, *pAfter, wide, abcA );
  632. pixels += wide;
  633. #else
  634. pixels += vgui::surface()->GetCharacterWidth( font, *p );
  635. #endif
  636. pBefore = p;
  637. p++;
  638. if ( *p )
  639. pAfter = p + 1;
  640. else
  641. pAfter = "\0";
  642. }
  643. return (int)ceil(pixels);
  644. }
  645. //-----------------------------------------------------------------------------
  646. // Purpose:
  647. // Input : font -
  648. // *str -
  649. // Output : int
  650. //-----------------------------------------------------------------------------
  651. int UTIL_ComputeStringWidth( vgui::HFont& font, const wchar_t *str )
  652. {
  653. float pixels = 0;
  654. const wchar_t *p = str;
  655. const wchar_t *pAfter = p + 1;
  656. const wchar_t *pBefore = L"\0";
  657. while ( *p )
  658. {
  659. #if USE_GETKERNEDCHARWIDTH
  660. float wide, abcA;
  661. vgui::surface()->GetKernedCharWidth( font, *p, *pBefore, *pAfter, wide, abcA );
  662. pixels += wide;
  663. #else
  664. pixels += vgui::surface()->GetCharacterWidth( font, *p );
  665. #endif
  666. pBefore = p;
  667. p++;
  668. if ( *p )
  669. pAfter = p + 1;
  670. else
  671. pAfter = L"\0";
  672. }
  673. return (int)ceil(pixels);
  674. }
  675. //-----------------------------------------------------------------------------
  676. // Purpose: Scans player names
  677. //Passes the player name to be checked in a KeyValues pointer
  678. //with the keyname "name"
  679. // - replaces '&' with '&&' so they will draw in the scoreboard
  680. // - replaces '#' at the start of the name with '*'
  681. //-----------------------------------------------------------------------------
  682. void UTIL_MakeSafeName( const char *oldName, char *newName, int newNameBufSize )
  683. {
  684. Assert( newNameBufSize >= sizeof(newName[0]) );
  685. int newpos = 0;
  686. for( const char *p=oldName; *p != 0 && newpos < newNameBufSize-1; p++ )
  687. {
  688. //check for a '#' char at the beginning
  689. if( p == oldName && *p == '#' )
  690. {
  691. newName[newpos] = '*';
  692. newpos++;
  693. }
  694. else if( *p == '%' )
  695. {
  696. // remove % chars
  697. newName[newpos] = '*';
  698. newpos++;
  699. }
  700. else if( *p == '&' )
  701. {
  702. //insert another & after this one
  703. if ( newpos+2 < newNameBufSize )
  704. {
  705. newName[newpos] = '&';
  706. newName[newpos+1] = '&';
  707. newpos+=2;
  708. }
  709. }
  710. else
  711. {
  712. newName[newpos] = *p;
  713. newpos++;
  714. }
  715. }
  716. newName[newpos] = 0;
  717. }
  718. //-----------------------------------------------------------------------------
  719. // Purpose: Scans player names and replaces characters that vgui won't
  720. // display properly
  721. // Input : *oldName - player name to be fixed up
  722. // Output : *char - static buffer with the safe name
  723. //-----------------------------------------------------------------------------
  724. const char * UTIL_SafeName( const char *oldName )
  725. {
  726. static char safeName[ MAX_PLAYER_NAME_LENGTH * 2 + 1 ];
  727. UTIL_MakeSafeName( oldName, safeName, sizeof( safeName ) );
  728. return safeName;
  729. }
  730. //-----------------------------------------------------------------------------
  731. // Purpose: Looks up key bindings for commands and replaces them in string.
  732. // %<commandname>% will get replaced with its bound control, e.g. %attack2%
  733. // Input buffer sizes are in bytes rather than unicode character count
  734. // for consistency with other APIs. If inbufsizebytes is 0 a NULL-terminated
  735. // input buffer is assumed, or you can pass the size of the input buffer if
  736. // not NULL-terminated.
  737. //
  738. // If actionset is other than GAME_ACTION_SET_NONE (the default), then a lookup is first
  739. // attempted for a Steam Controller binding in the given action set. If none if found, fallback
  740. // is to the usual keyboard binding path.
  741. //-----------------------------------------------------------------------------
  742. void UTIL_ReplaceKeyBindings( const wchar_t *inbuf, int inbufsizebytes, OUT_Z_BYTECAP(outbufsizebytes) wchar_t *outbuf, int outbufsizebytes, GameActionSet_t actionset )
  743. {
  744. Assert( outbufsizebytes >= sizeof(outbuf[0]) );
  745. // copy to a new buf if there are vars
  746. outbuf[0]=0;
  747. if ( !inbuf || !inbuf[0] )
  748. return;
  749. int pos = 0;
  750. const wchar_t *inbufend = NULL;
  751. if ( inbufsizebytes > 0 )
  752. {
  753. inbufend = inbuf + ( inbufsizebytes / 2 );
  754. }
  755. while( inbuf != inbufend && *inbuf != 0 )
  756. {
  757. // check for variables
  758. if ( *inbuf == '%' )
  759. {
  760. ++inbuf;
  761. const wchar_t *end = wcschr( inbuf, '%' );
  762. if ( end && ( end != inbuf ) ) // make sure we handle %% in the string, which should be treated in the output as %
  763. {
  764. wchar_t token[64];
  765. wcsncpy( token, inbuf, end - inbuf );
  766. token[end - inbuf] = 0;
  767. inbuf += end - inbuf;
  768. // lookup key names
  769. char binding[64];
  770. g_pVGuiLocalize->ConvertUnicodeToANSI( token, binding, sizeof(binding) );
  771. // Find a Steam Controller mapping, if an action set was specified.
  772. const wchar_t* sc_origin = nullptr;
  773. if ( actionset != GAME_ACTION_SET_NONE)
  774. {
  775. auto origin = g_pInputSystem->GetSteamControllerActionOrigin( *binding == '+' ? binding + 1 : binding, actionset );
  776. if ( origin != k_EControllerActionOrigin_None )
  777. {
  778. sc_origin = g_pInputSystem->GetSteamControllerDescriptionForActionOrigin( origin );
  779. }
  780. }
  781. // Find also the keyboard mapping
  782. const char *key = engine->Key_LookupBinding( *binding == '+' ? binding + 1 : binding );
  783. if ( !key )
  784. {
  785. key = IsX360() ? "" : "< not bound >";
  786. }
  787. //!! change some key names into better names
  788. char friendlyName[64];
  789. bool bAddBrackets = false;
  790. if ( IsX360() )
  791. {
  792. if ( !key || !key[0] )
  793. {
  794. Q_snprintf( friendlyName, sizeof(friendlyName), "#GameUI_None" );
  795. bAddBrackets = true;
  796. }
  797. else
  798. {
  799. Q_snprintf( friendlyName, sizeof(friendlyName), "#GameUI_KeyNames_%s", key );
  800. }
  801. }
  802. else
  803. {
  804. Q_snprintf( friendlyName, sizeof(friendlyName), "%s", key );
  805. }
  806. Q_strupr( friendlyName );
  807. const wchar_t* locName = nullptr;
  808. // If we got a Steam Controller key description, use that, otherwise use the (possibly localized) key name
  809. if ( sc_origin )
  810. {
  811. locName = sc_origin;
  812. }
  813. else
  814. {
  815. locName = g_pVGuiLocalize->Find( friendlyName );
  816. }
  817. if ( !locName || wcslen(locName) <= 0)
  818. {
  819. g_pVGuiLocalize->ConvertANSIToUnicode( friendlyName, token, sizeof(token) );
  820. outbuf[pos] = '\0';
  821. wcscat( outbuf, token );
  822. pos += wcslen(token);
  823. }
  824. else
  825. {
  826. outbuf[pos] = '\0';
  827. if ( bAddBrackets )
  828. {
  829. wcscat( outbuf, L"[" );
  830. pos += 1;
  831. }
  832. wcscat( outbuf, locName );
  833. pos += wcslen(locName);
  834. if ( bAddBrackets )
  835. {
  836. wcscat( outbuf, L"]" );
  837. pos += 1;
  838. }
  839. }
  840. }
  841. else
  842. {
  843. outbuf[pos] = *inbuf;
  844. ++pos;
  845. }
  846. }
  847. else
  848. {
  849. outbuf[pos] = *inbuf;
  850. ++pos;
  851. }
  852. ++inbuf;
  853. }
  854. outbuf[pos] = '\0';
  855. }
  856. //-----------------------------------------------------------------------------
  857. // Purpose:
  858. // Input : *filename -
  859. // *pLength -
  860. // Output : byte
  861. //-----------------------------------------------------------------------------
  862. byte *UTIL_LoadFileForMe( const char *filename, int *pLength )
  863. {
  864. byte *buffer;
  865. FileHandle_t file;
  866. file = filesystem->Open( filename, "rb", "GAME" );
  867. if ( FILESYSTEM_INVALID_HANDLE == file )
  868. {
  869. if ( pLength ) *pLength = 0;
  870. return NULL;
  871. }
  872. int size = filesystem->Size( file );
  873. buffer = new byte[ size + 1 ];
  874. if ( !buffer )
  875. {
  876. Warning( "UTIL_LoadFileForMe: Couldn't allocate buffer of size %i for file %s\n", size + 1, filename );
  877. filesystem->Close( file );
  878. return NULL;
  879. }
  880. filesystem->Read( buffer, size, file );
  881. filesystem->Close( file );
  882. // Ensure null terminator
  883. buffer[ size ] =0;
  884. if ( pLength )
  885. {
  886. *pLength = size;
  887. }
  888. return buffer;
  889. }
  890. //-----------------------------------------------------------------------------
  891. // Purpose:
  892. // Input : *buffer -
  893. //-----------------------------------------------------------------------------
  894. void UTIL_FreeFile( byte *buffer )
  895. {
  896. delete[] buffer;
  897. }
  898. //-----------------------------------------------------------------------------
  899. // Compute distance fade
  900. //-----------------------------------------------------------------------------
  901. static unsigned char ComputeDistanceFade( C_BaseEntity *pEntity, float flMinDist, float flMaxDist )
  902. {
  903. if ((flMinDist <= 0) && (flMaxDist <= 0))
  904. return 255;
  905. if( flMinDist > flMaxDist )
  906. {
  907. ::V_swap( flMinDist, flMaxDist );
  908. }
  909. // If a negative value is provided for the min fade distance, then base it off the max.
  910. if( flMinDist < 0 )
  911. {
  912. flMinDist = flMaxDist - 400;
  913. if( flMinDist < 0 )
  914. {
  915. flMinDist = 0;
  916. }
  917. }
  918. flMinDist *= flMinDist;
  919. flMaxDist *= flMaxDist;
  920. float flCurrentDistanceSq = CurrentViewOrigin().DistToSqr( pEntity->WorldSpaceCenter() );
  921. C_BasePlayer *pLocal = C_BasePlayer::GetLocalPlayer();
  922. if ( pLocal )
  923. {
  924. float flDistFactor = pLocal->GetFOVDistanceAdjustFactor();
  925. flCurrentDistanceSq *= flDistFactor * flDistFactor;
  926. }
  927. // If I'm inside the minimum range than don't resort to alpha trickery
  928. if ( flCurrentDistanceSq <= flMinDist )
  929. return 255;
  930. if ( flCurrentDistanceSq >= flMaxDist )
  931. return 0;
  932. // NOTE: Because of the if-checks above, flMinDist != flMinDist here
  933. float flFalloffFactor = 255.0f / (flMaxDist - flMinDist);
  934. int nAlpha = flFalloffFactor * (flMaxDist - flCurrentDistanceSq);
  935. return clamp( nAlpha, 0, 255 );
  936. }
  937. //-----------------------------------------------------------------------------
  938. // Compute fade amount
  939. //-----------------------------------------------------------------------------
  940. unsigned char UTIL_ComputeEntityFade( C_BaseEntity *pEntity, float flMinDist, float flMaxDist, float flFadeScale )
  941. {
  942. unsigned char nAlpha = 255;
  943. // If we're taking devshots, don't fade props at all
  944. if ( g_MakingDevShots || cl_leveloverview.GetFloat() > 0 )
  945. return 255;
  946. #ifdef _DEBUG
  947. if ( r_FadeProps.GetBool() )
  948. #endif
  949. {
  950. nAlpha = ComputeDistanceFade( pEntity, flMinDist, flMaxDist );
  951. // NOTE: This computation for the center + radius is invalid!
  952. // The center of the sphere is at the center of the OBB, which is not necessarily
  953. // at the render origin. But it should be close enough.
  954. Vector vecMins, vecMaxs;
  955. pEntity->GetRenderBounds( vecMins, vecMaxs );
  956. float flRadius = vecMins.DistTo( vecMaxs ) * 0.5f;
  957. Vector vecAbsCenter;
  958. if ( modelinfo->GetModelType( pEntity->GetModel() ) == mod_brush )
  959. {
  960. Vector vecRenderMins, vecRenderMaxs;
  961. pEntity->GetRenderBoundsWorldspace( vecRenderMins, vecRenderMaxs );
  962. VectorAdd( vecRenderMins, vecRenderMaxs, vecAbsCenter );
  963. vecAbsCenter *= 0.5f;
  964. }
  965. else
  966. {
  967. vecAbsCenter = pEntity->GetRenderOrigin();
  968. }
  969. unsigned char nGlobalAlpha = IsXbox() ? 255 : modelinfo->ComputeLevelScreenFade( vecAbsCenter, flRadius, flFadeScale );
  970. unsigned char nDistAlpha;
  971. if ( !engine->IsLevelMainMenuBackground() )
  972. {
  973. nDistAlpha = modelinfo->ComputeViewScreenFade( vecAbsCenter, flRadius, flFadeScale );
  974. }
  975. else
  976. {
  977. nDistAlpha = 255;
  978. }
  979. if ( nDistAlpha < nGlobalAlpha )
  980. {
  981. nGlobalAlpha = nDistAlpha;
  982. }
  983. if ( nGlobalAlpha < nAlpha )
  984. {
  985. nAlpha = nGlobalAlpha;
  986. }
  987. }
  988. return nAlpha;
  989. }
  990. //-----------------------------------------------------------------------------
  991. // Purpose: Given a vector, clamps the scalar axes to MAX_COORD_FLOAT ranges from worldsize.h
  992. // Input : *pVecPos -
  993. //-----------------------------------------------------------------------------
  994. void UTIL_BoundToWorldSize( Vector *pVecPos )
  995. {
  996. Assert( pVecPos );
  997. for ( int i = 0; i < 3; ++i )
  998. {
  999. (*pVecPos)[ i ] = clamp( (*pVecPos)[ i ], MIN_COORD_FLOAT, MAX_COORD_FLOAT );
  1000. }
  1001. }
  1002. #ifdef _X360
  1003. #define MAP_KEY_FILE_DIR "cfg"
  1004. #else
  1005. #define MAP_KEY_FILE_DIR "media"
  1006. #endif
  1007. //-----------------------------------------------------------------------------
  1008. // Purpose: Returns the filename to count map loads in
  1009. //-----------------------------------------------------------------------------
  1010. bool UTIL_GetMapLoadCountFileName( const char *pszFilePrependName, char *pszBuffer, int iBuflen )
  1011. {
  1012. if ( IsX360() )
  1013. {
  1014. #ifdef _X360
  1015. if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED )
  1016. return false;
  1017. #endif
  1018. }
  1019. if ( IsX360() )
  1020. {
  1021. Q_snprintf( pszBuffer, iBuflen, "%s:/%s", MAP_KEY_FILE_DIR, pszFilePrependName );
  1022. }
  1023. else
  1024. {
  1025. Q_snprintf( pszBuffer, iBuflen, "%s/%s", MAP_KEY_FILE_DIR, pszFilePrependName );
  1026. }
  1027. return true;
  1028. }
  1029. #ifdef TF_CLIENT_DLL
  1030. #define MAP_KEY_FILE "viewed.res"
  1031. #else
  1032. #define MAP_KEY_FILE "mapkeys.res"
  1033. #endif
  1034. void UTIL_IncrementMapKey( const char *pszCustomKey )
  1035. {
  1036. if ( !pszCustomKey )
  1037. return;
  1038. char szFilename[ _MAX_PATH ];
  1039. if ( !UTIL_GetMapLoadCountFileName( MAP_KEY_FILE, szFilename, _MAX_PATH ) )
  1040. return;
  1041. int iCount = 1;
  1042. KeyValues *kvMapLoadFile = new KeyValues( MAP_KEY_FILE );
  1043. if ( kvMapLoadFile )
  1044. {
  1045. kvMapLoadFile->LoadFromFile( g_pFullFileSystem, szFilename, "MOD" );
  1046. char mapname[MAX_MAP_NAME];
  1047. Q_FileBase( engine->GetLevelName(), mapname, sizeof( mapname) );
  1048. Q_strlower( mapname );
  1049. // Increment existing, or add a new one
  1050. KeyValues *pMapKey = kvMapLoadFile->FindKey( mapname );
  1051. if ( pMapKey )
  1052. {
  1053. iCount = pMapKey->GetInt( pszCustomKey, 0 ) + 1;
  1054. pMapKey->SetInt( pszCustomKey, iCount );
  1055. }
  1056. else
  1057. {
  1058. KeyValues *pNewKey = new KeyValues( mapname );
  1059. if ( pNewKey )
  1060. {
  1061. pNewKey->SetString( pszCustomKey, "1" );
  1062. kvMapLoadFile->AddSubKey( pNewKey );
  1063. }
  1064. }
  1065. // Write it out
  1066. // force create this directory incase it doesn't exist
  1067. filesystem->CreateDirHierarchy( MAP_KEY_FILE_DIR, "MOD");
  1068. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  1069. kvMapLoadFile->RecursiveSaveToFile( buf, 0 );
  1070. g_pFullFileSystem->WriteFile( szFilename, "MOD", buf );
  1071. kvMapLoadFile->deleteThis();
  1072. }
  1073. if ( IsX360() )
  1074. {
  1075. #ifdef _X360
  1076. xboxsystem->FinishContainerWrites();
  1077. #endif
  1078. }
  1079. }
  1080. int UTIL_GetMapKeyCount( const char *pszCustomKey )
  1081. {
  1082. if ( !pszCustomKey )
  1083. return 0;
  1084. char szFilename[ _MAX_PATH ];
  1085. if ( !UTIL_GetMapLoadCountFileName( MAP_KEY_FILE, szFilename, _MAX_PATH ) )
  1086. return 0;
  1087. int iCount = 0;
  1088. KeyValues *kvMapLoadFile = new KeyValues( MAP_KEY_FILE );
  1089. if ( kvMapLoadFile )
  1090. {
  1091. // create an empty file if none exists
  1092. if ( !g_pFullFileSystem->FileExists( szFilename, "MOD" ) )
  1093. {
  1094. // force create this directory incase it doesn't exist
  1095. filesystem->CreateDirHierarchy( MAP_KEY_FILE_DIR, "MOD");
  1096. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  1097. g_pFullFileSystem->WriteFile( szFilename, "MOD", buf );
  1098. }
  1099. kvMapLoadFile->LoadFromFile( g_pFullFileSystem, szFilename, "MOD" );
  1100. char mapname[MAX_MAP_NAME];
  1101. Q_FileBase( engine->GetLevelName(), mapname, sizeof( mapname) );
  1102. Q_strlower( mapname );
  1103. KeyValues *pMapKey = kvMapLoadFile->FindKey( mapname );
  1104. if ( pMapKey )
  1105. {
  1106. iCount = pMapKey->GetInt( pszCustomKey );
  1107. }
  1108. kvMapLoadFile->deleteThis();
  1109. }
  1110. return iCount;
  1111. }
  1112. bool UTIL_HasLoadedAnyMap()
  1113. {
  1114. char szFilename[ _MAX_PATH ];
  1115. if ( !UTIL_GetMapLoadCountFileName( MAP_KEY_FILE, szFilename, _MAX_PATH ) )
  1116. return false;
  1117. return g_pFullFileSystem->FileExists( szFilename, "MOD" );
  1118. }