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.

302 lines
11 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "filesystem.h"
  8. #include "portal_gamestats.h"
  9. #include "util_shared.h"
  10. #include "prop_portal_shared.h"
  11. #include "utlstring.h"
  12. #ifdef PORTAL_GAMESTATS_VERBOSE //this shouldn't be in release versions, and most loading/saving code is only enabled with this defined
  13. static ConVar portalstats_visdeaths( "portalstats_visdeaths", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
  14. static ConVar portalstats_visjumps( "portalstats_visjumps", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
  15. static ConVar portalstats_visusages( "portalstats_visusages", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
  16. static ConVar portalstats_visstucks( "portalstats_visstucks", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
  17. static ConVar portalstats_visplacement( "portalstats_visplacement", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
  18. class CPortalGameStatsVisualizer : public CPortalGameStats, public CAutoGameSystemPerFrame
  19. {
  20. public:
  21. float m_fRefreshTimer;
  22. void FrameUpdatePostEntityThink();
  23. void LevelInitPreEntity();
  24. void PrepareLevelData( void );
  25. CUtlVector<QAngle> m_PortalPlacementAngles;
  26. CPortalGameStatsVisualizer( void )
  27. : m_fRefreshTimer( 0.0f )
  28. { };
  29. };
  30. CPortalGameStatsVisualizer s_GameStatsVisualizer;
  31. void CPortalGameStatsVisualizer::LevelInitPreEntity()
  32. {
  33. m_fRefreshTimer = gpGlobals->curtime;
  34. PrepareLevelData();
  35. }
  36. void CPortalGameStatsVisualizer::PrepareLevelData( void )
  37. {
  38. m_pCurrentMapStats = FindOrAddMapStats( STRING( gpGlobals->mapname ) );
  39. m_PortalPlacementAngles.SetSize( m_pCurrentMapStats->m_pPlacements->Count() );
  40. for( int i = m_pCurrentMapStats->m_pPlacements->Count(); --i >= 0; )
  41. {
  42. Portal_Gamestats_LevelStats_t::PortalPlacement_t &PlacementStat = m_pCurrentMapStats->m_pPlacements->Element( i );
  43. trace_t tr;
  44. // Trace to see where the portal hit
  45. Vector vDirection = PlacementStat.ptPlacementPosition - PlacementStat.ptPlayerFiredFrom;
  46. UTIL_TraceLine( PlacementStat.ptPlayerFiredFrom, PlacementStat.ptPlayerFiredFrom + (vDirection * 100000.0f), MASK_SHOT_PORTAL, NULL, &tr );
  47. Vector vUp( 0.0f, 0.0f, 1.0f );
  48. if( ( tr.plane.normal.x > -0.001f && tr.plane.normal.x < 0.001f ) && ( tr.plane.normal.y > -0.001f && tr.plane.normal.y < 0.001f ) )
  49. {
  50. //plane is a level floor/ceiling
  51. vUp = vDirection;
  52. }
  53. VectorAngles( tr.plane.normal, vUp, m_PortalPlacementAngles[i] );
  54. }
  55. }
  56. #define PORTALSTATSVISUALIZER_REFRESHTIME 1.0f
  57. #define PORTALSTATSVISUALIZER_DISPLAYTIME (PORTALSTATSVISUALIZER_REFRESHTIME + 0.05f)
  58. void CPortalGameStatsVisualizer::FrameUpdatePostEntityThink()
  59. {
  60. if( m_fRefreshTimer > gpGlobals->curtime )
  61. return;
  62. //refresh now
  63. m_fRefreshTimer = gpGlobals->curtime + PORTALSTATSVISUALIZER_REFRESHTIME;
  64. static const Vector vPlayerBoxMins( -16.0f, -16.0f, 0.0f ), vPlayerBoxMaxs( 16.0f, 16.0f, 72.0f ), vPlayerTextOffset( 0.0f, 0.0f, 36.0f );
  65. static const Vector vSmallBoxMins( -7.5f, -7.5f, -7.5f ), vSmallBoxMaxs( 7.5f, 7.5f, 7.5f );
  66. if( portalstats_visdeaths.GetBool() )
  67. {
  68. for( int i = m_pCurrentMapStats->m_pDeaths->Count(); --i >= 0; )
  69. {
  70. Portal_Gamestats_LevelStats_t::PlayerDeaths_t &DeathStat = m_pCurrentMapStats->m_pDeaths->Element( i );
  71. NDebugOverlay::Box( DeathStat.ptPositionOfDeath, vPlayerBoxMins, vPlayerBoxMaxs, 255, 0, 0, 100, PORTALSTATSVISUALIZER_DISPLAYTIME );
  72. NDebugOverlay::Text( DeathStat.ptPositionOfDeath + vPlayerTextOffset, DeathStat.szAttackerClassName, true, PORTALSTATSVISUALIZER_DISPLAYTIME );
  73. }
  74. }
  75. if( portalstats_visjumps.GetBool() )
  76. {
  77. for( int i = m_pCurrentMapStats->m_pJumps->Count(); --i >= 0; )
  78. {
  79. Portal_Gamestats_LevelStats_t::JumpEvent_t &JumpStat = m_pCurrentMapStats->m_pJumps->Element( i );
  80. NDebugOverlay::Box( JumpStat.ptPlayerPositionAtJumpStart, vPlayerBoxMins, vPlayerBoxMaxs, 0, 255, 0, 100, PORTALSTATSVISUALIZER_DISPLAYTIME );
  81. NDebugOverlay::Line( JumpStat.ptPlayerPositionAtJumpStart + vPlayerTextOffset, JumpStat.ptPlayerPositionAtJumpStart + vPlayerTextOffset + (JumpStat.vPlayerVelocityAtJumpStart), 0, 255, 0, false, PORTALSTATSVISUALIZER_REFRESHTIME );
  82. }
  83. }
  84. if( portalstats_visusages.GetBool() )
  85. {
  86. for( int i = m_pCurrentMapStats->m_pUseEvents->Count(); --i >= 0; )
  87. {
  88. Portal_Gamestats_LevelStats_t::PlayerUse_t &UseEvent = m_pCurrentMapStats->m_pUseEvents->Element( i );
  89. NDebugOverlay::Box( UseEvent.ptTraceStart, vSmallBoxMins, vSmallBoxMaxs, 0, 0, 255, 100, PORTALSTATSVISUALIZER_DISPLAYTIME );
  90. NDebugOverlay::Line( UseEvent.ptTraceStart, UseEvent.ptTraceStart + (UseEvent.vTraceDelta * 100.0f), 0, 0, 255, false, PORTALSTATSVISUALIZER_DISPLAYTIME );
  91. NDebugOverlay::Text( UseEvent.ptTraceStart, UseEvent.szUseEntityClassName, true, PORTALSTATSVISUALIZER_DISPLAYTIME );
  92. }
  93. }
  94. if( portalstats_visstucks.GetBool() )
  95. {
  96. for( int i = m_pCurrentMapStats->m_pStuckSpots->Count(); --i >= 0; )
  97. {
  98. Portal_Gamestats_LevelStats_t::StuckEvent_t &StuckEvent = m_pCurrentMapStats->m_pStuckSpots->Element( i );
  99. NDebugOverlay::Box( StuckEvent.ptPlayerPosition, vPlayerBoxMins, vPlayerBoxMaxs, 255, 0, 255, 100, PORTALSTATSVISUALIZER_DISPLAYTIME );
  100. }
  101. }
  102. if( portalstats_visplacement.GetBool() )
  103. {
  104. Assert( m_pCurrentMapStats->m_pPlacements->Count() == m_PortalPlacementAngles.Count() );
  105. for( int i = m_pCurrentMapStats->m_pPlacements->Count(); --i >= 0; )
  106. {
  107. Portal_Gamestats_LevelStats_t::PortalPlacement_t &PlacementStat = m_pCurrentMapStats->m_pPlacements->Element( i );
  108. unsigned char brightnessval;
  109. if( PlacementStat.iSuccessCode == 0 )
  110. brightnessval = 255;//worked
  111. else
  112. brightnessval = 64;//failed
  113. NDebugOverlay::BoxAngles( PlacementStat.ptPlacementPosition, CProp_Portal_Shared::vLocalMins, CProp_Portal_Shared::vLocalMaxs, m_PortalPlacementAngles[i], 0, brightnessval, brightnessval, 100, PORTALSTATSVISUALIZER_DISPLAYTIME );
  114. NDebugOverlay::Box( PlacementStat.ptPlayerFiredFrom, vSmallBoxMins, vSmallBoxMaxs, 0, brightnessval, brightnessval, 100, PORTALSTATSVISUALIZER_DISPLAYTIME );
  115. NDebugOverlay::Line( PlacementStat.ptPlayerFiredFrom, PlacementStat.ptPlacementPosition, 0, brightnessval, brightnessval, false, PORTALSTATSVISUALIZER_DISPLAYTIME );
  116. }
  117. }
  118. }
  119. static CUtlVector<CUtlString> s_PortalStatsDataFileList;
  120. static void PortalStats_UpdateFileList( void )
  121. {
  122. s_PortalStatsDataFileList.RemoveAll();
  123. FileFindHandle_t fileHandle;
  124. const char *pszFileName = filesystem->FindFirst( "customstats/*.dat", &fileHandle );
  125. while( pszFileName )
  126. {
  127. // Skip it if it's a directory
  128. if( !filesystem->FindIsDirectory( fileHandle ) )
  129. {
  130. char szRelativeFileName[_MAX_PATH];
  131. Q_snprintf( szRelativeFileName, sizeof( szRelativeFileName ), "customstats/%s", pszFileName );
  132. // Only load files from the current mod's directory hierarchy
  133. if( filesystem->FileExists( szRelativeFileName, "MOD" ) )
  134. {
  135. int index = s_PortalStatsDataFileList.AddToTail();
  136. s_PortalStatsDataFileList[index].Set( pszFileName );
  137. }
  138. }
  139. pszFileName = filesystem->FindNext( fileHandle );
  140. }
  141. filesystem->FindClose( fileHandle );
  142. }
  143. static int PortalStats_LoadFile_f_CompletionFunc( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  144. {
  145. PortalStats_UpdateFileList();
  146. int iRetCount = MIN( s_PortalStatsDataFileList.Count(), COMMAND_COMPLETION_MAXITEMS );
  147. for( int i = 0; i != iRetCount; ++i )
  148. {
  149. Q_snprintf( commands[i], COMMAND_COMPLETION_ITEM_LENGTH, "%s %s", partial, s_PortalStatsDataFileList[i].Get() );
  150. }
  151. return iRetCount;
  152. }
  153. static char s_szCurrentlyLoadedStatsFile[256] = "";
  154. static void PortalStats_LoadFileHelper( const char *szFileName )
  155. {
  156. CUtlBuffer statsfileContents;
  157. char szRelativeName[256];
  158. Q_snprintf( szRelativeName, sizeof( szRelativeName ), "customstats/%s", szFileName );
  159. if( filesystem->ReadFile( szRelativeName, "MOD", statsfileContents ) )
  160. {
  161. Q_strncpy( s_szCurrentlyLoadedStatsFile, szFileName, sizeof( s_szCurrentlyLoadedStatsFile ) );
  162. s_GameStatsVisualizer.Clear();
  163. s_GameStatsVisualizer.LoadCustomDataFromBuffer( statsfileContents );
  164. s_GameStatsVisualizer.PrepareLevelData();
  165. s_GameStatsVisualizer.m_fRefreshTimer = 0.0f; //start drawing new data right away
  166. }
  167. }
  168. static void PortalStats_LoadFile_f( const CCommand &args )
  169. {
  170. if( args.ArgC() > 1 )
  171. {
  172. PortalStats_LoadFileHelper( args[1] );
  173. }
  174. }
  175. static void PortalStats_LoadNextFile_f( void )
  176. {
  177. PortalStats_UpdateFileList();
  178. if( s_PortalStatsDataFileList.Count() == 0 )
  179. return;
  180. int i;
  181. for( i = s_PortalStatsDataFileList.Count(); --i >= 0; )
  182. {
  183. if( Q_stricmp( s_PortalStatsDataFileList[i].Get(), s_szCurrentlyLoadedStatsFile ) == 0 )
  184. break;
  185. }
  186. if( i < 0 )
  187. {
  188. //currently loaded file not found, just load the first
  189. PortalStats_LoadFileHelper( s_PortalStatsDataFileList[0] );
  190. }
  191. else
  192. {
  193. ++i;
  194. if( i == s_PortalStatsDataFileList.Count() )
  195. {
  196. DevMsg( "PortalStats_LoadNextFile looping to first file in directory.\n" );
  197. NDebugOverlay::ScreenText( 0.3f, 0.5f, "PortalStats_LoadNextFile looping to first file in directory.", 255, 255, 255, 255, 2.0f );
  198. i = 0;
  199. }
  200. PortalStats_LoadFileHelper( s_PortalStatsDataFileList[i] );
  201. }
  202. }
  203. static void PortalStats_LoadPrevFile_f( void )
  204. {
  205. PortalStats_UpdateFileList();
  206. if( s_PortalStatsDataFileList.Count() == 0 )
  207. return;
  208. int i;
  209. for( i = s_PortalStatsDataFileList.Count(); --i >= 0; )
  210. {
  211. if( Q_stricmp( s_PortalStatsDataFileList[i].Get(), s_szCurrentlyLoadedStatsFile ) == 0 )
  212. break;
  213. }
  214. if( i < 0 )
  215. {
  216. //currently loaded file not found, just load the first
  217. PortalStats_LoadFileHelper( s_PortalStatsDataFileList[0] );
  218. }
  219. else
  220. {
  221. --i;
  222. if( i < 0 )
  223. {
  224. i = s_PortalStatsDataFileList.Count() - 1;
  225. DevMsg( "PortalStats_LoadPrevFile looping to last file in directory.\n" );
  226. NDebugOverlay::ScreenText( 0.3f, 0.5f, "PortalStats_LoadPrevFile looping to last file in directory.", 255, 255, 255, 255, 2.0f );
  227. }
  228. PortalStats_LoadFileHelper( s_PortalStatsDataFileList[i] );
  229. }
  230. }
  231. //static ConCommand Portal_LoadCustomStatsFile("portal_loadcustomstatsfile", Portal_LoadCustomStatsFile_f, "Load a custom stats file for visualizing.", FCVAR_DONTRECORD, Portal_LoadCustomStatsFile_f_CompletionFunc );
  232. static ConCommand PortalStats_LoadFile("portalstats_loadfile", PortalStats_LoadFile_f, "Load a custom stats file for visualizing.", FCVAR_DONTRECORD, PortalStats_LoadFile_f_CompletionFunc );
  233. static ConCommand PortalStats_LoadNextFile("portalstats_loadnextfile", PortalStats_LoadNextFile_f, "Load the next custom stats file for visualizing.", FCVAR_DONTRECORD );
  234. static ConCommand PortalStats_LoadPrevFile("portalstats_loadprevfile", PortalStats_LoadPrevFile_f, "Load the next custom stats file for visualizing.", FCVAR_DONTRECORD );
  235. #endif //#ifdef PORTAL_GAMESTATS_VERBOSE