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.

532 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #ifndef EP2_GAMESTATS_H
  7. #define EP2_GAMESTATS_H
  8. #ifdef _WIN32
  9. #pragma once
  10. #endif
  11. #include "ep1_gamestats.h"
  12. #include "tier1/utlstring.h"
  13. // EP2 Game Stats
  14. enum Ep2GameStatsVersions_t
  15. {
  16. EP2_GAMESTATS_FILE_VERSION_01 = 001,
  17. EP2_GAMESTATS_FILE_VERSION_02 = 002,
  18. EP2_GAMESTATS_CURRENT_VERSION = EP2_GAMESTATS_FILE_VERSION_02,
  19. };
  20. enum Ep2GameStatsLumpIds_t
  21. {
  22. EP2STATS_LUMP_HEADER = 1,
  23. EP2STATS_LUMP_DEATH,
  24. EP2STATS_LUMP_NPC,
  25. EP2STATS_LUMP_WEAPON,
  26. EP2STATS_LUMP_SAVEGAMEINFO,
  27. EP2STATS_LUMP_TAG,
  28. EP2STATS_LUMP_GENERIC,
  29. EP2_MAX_LUMP_COUNT
  30. };
  31. // EP2 Game Level Stats Data
  32. struct Ep2LevelStats_t
  33. {
  34. public:
  35. enum FloatCounterTypes_t
  36. {
  37. COUNTER_DAMAGETAKEN = 0,
  38. NUM_FLOATCOUNTER_TYPES,
  39. };
  40. enum IntCounterTypes_t
  41. {
  42. COUNTER_CRATESSMASHED = 0,
  43. COUNTER_OBJECTSPUNTED,
  44. COUNTER_VEHICULARHOMICIDES,
  45. COUNTER_DISTANCE_INVEHICLE,
  46. COUNTER_DISTANCE_ONFOOT,
  47. COUNTER_DISTANCE_ONFOOTSPRINTING,
  48. COUNTER_FALLINGDEATHS,
  49. COUNTER_VEHICLE_OVERTURNED,
  50. COUNTER_LOADGAME_STILLALIVE,
  51. COUNTER_LOADS,
  52. COUNTER_SAVES,
  53. COUNTER_GODMODES,
  54. COUNTER_NOCLIPS,
  55. NUM_INTCOUNTER_TYPES,
  56. };
  57. Ep2LevelStats_t() :
  58. m_bInitialized( false ),
  59. m_flLevelStartTime( 0.0f )
  60. {
  61. Q_memset( m_IntCounters, 0, sizeof( m_IntCounters ) );
  62. Q_memset( m_FloatCounters, 0, sizeof( m_FloatCounters ) );
  63. }
  64. ~Ep2LevelStats_t()
  65. {
  66. }
  67. Ep2LevelStats_t( const Ep2LevelStats_t &other )
  68. {
  69. m_bInitialized = other.m_bInitialized;
  70. m_flLevelStartTime = other.m_flLevelStartTime;
  71. m_Header = other.m_Header;
  72. m_aPlayerDeaths = other.m_aPlayerDeaths;
  73. Q_memcpy( m_IntCounters, other.m_IntCounters, sizeof( m_IntCounters ) );
  74. Q_memcpy( m_FloatCounters, other.m_FloatCounters, sizeof( m_FloatCounters ) );
  75. int i;
  76. for ( i = other.m_dictEntityDeaths.First(); i != other.m_dictEntityDeaths.InvalidIndex(); i = other.m_dictEntityDeaths.Next( i ) )
  77. {
  78. m_dictEntityDeaths.Insert( other.m_dictEntityDeaths.GetElementName( i ), other.m_dictEntityDeaths[ i ] );
  79. }
  80. for ( i = other.m_dictWeapons.First(); i != other.m_dictWeapons.InvalidIndex(); i = other.m_dictWeapons.Next( i ) )
  81. {
  82. m_dictWeapons.Insert( other.m_dictWeapons.GetElementName( i ), other.m_dictWeapons[ i ] );
  83. }
  84. m_SaveGameInfo = other.m_SaveGameInfo;
  85. }
  86. // Create and destroy.
  87. void Init( const char *pszMapName, float flStartTime, char const *pchTag, int nMapVersion )
  88. {
  89. // Initialize.
  90. m_Header.m_iVersion = EP2_GAMESTATS_CURRENT_VERSION;
  91. Q_strncpy( m_Header.m_szMapName, pszMapName, sizeof( m_Header.m_szMapName ) );
  92. m_Header.m_flTime = 0.0f;
  93. // Start the level timer.
  94. m_flLevelStartTime = flStartTime;
  95. Q_strncpy( m_Tag.m_szTagText, pchTag, sizeof( m_Tag.m_szTagText ) );
  96. m_Tag.m_nMapVersion = nMapVersion;
  97. }
  98. void Shutdown( float flEndTime )
  99. {
  100. m_Header.m_flTime = flEndTime - m_flLevelStartTime;
  101. }
  102. void AppendToBuffer( CUtlBuffer &SaveBuffer )
  103. {
  104. // Always write out as current version
  105. m_Header.m_iVersion = EP2_GAMESTATS_CURRENT_VERSION;
  106. // Write out the lumps.
  107. CBaseGameStats::AppendLump( EP2_MAX_LUMP_COUNT, SaveBuffer, EP2STATS_LUMP_HEADER, 1, sizeof( Ep2LevelStats_t::LevelHeader_t ), static_cast<void*>( &m_Header ) );
  108. CBaseGameStats::AppendLump( EP2_MAX_LUMP_COUNT, SaveBuffer, EP2STATS_LUMP_TAG, 1, sizeof( Ep2LevelStats_t::Tag_t ), static_cast< void * >( &m_Tag ) );
  109. CBaseGameStats::AppendLump( EP2_MAX_LUMP_COUNT, SaveBuffer, EP2STATS_LUMP_DEATH, m_aPlayerDeaths.Count(), sizeof( Ep2LevelStats_t::PlayerDeathsLump_t ), static_cast<void*>( m_aPlayerDeaths.Base() ) );
  110. {
  111. CUtlBuffer buf;
  112. buf.Put( (const void *)m_IntCounters, sizeof( m_IntCounters ) );
  113. buf.Put( (const void *)m_FloatCounters, sizeof( m_FloatCounters ) );
  114. buf.PutInt( m_dictEntityDeaths.Count() );
  115. for ( int i = m_dictEntityDeaths.First(); i != m_dictEntityDeaths.InvalidIndex(); i = m_dictEntityDeaths.Next( i ) )
  116. {
  117. buf.PutString( m_dictEntityDeaths.GetElementName( i ) );
  118. buf.Put( (const void *)&m_dictEntityDeaths[ i ], sizeof( Ep2LevelStats_t::EntityDeathsLump_t ) );
  119. }
  120. CBaseGameStats::AppendLump( EP2_MAX_LUMP_COUNT, SaveBuffer, EP2STATS_LUMP_NPC, 1, buf.TellPut(), buf.Base() );
  121. }
  122. {
  123. CUtlBuffer buf;
  124. buf.PutInt( m_dictWeapons.Count() );
  125. for ( int i = m_dictWeapons.First(); i != m_dictWeapons.InvalidIndex(); i = m_dictWeapons.Next( i ) )
  126. {
  127. buf.PutString( m_dictWeapons.GetElementName( i ) );
  128. buf.Put( (const void *)&m_dictWeapons[ i ], sizeof( Ep2LevelStats_t::WeaponLump_t ) );
  129. }
  130. CBaseGameStats::AppendLump( EP2_MAX_LUMP_COUNT, SaveBuffer, EP2STATS_LUMP_WEAPON, 1, buf.TellPut(), buf.Base() );
  131. }
  132. {
  133. CUtlBuffer buf;
  134. buf.PutString( m_SaveGameInfo.m_sCurrentSaveFile.String() );
  135. buf.PutInt( m_SaveGameInfo.m_nCurrentSaveFileTime );
  136. buf.PutInt( m_SaveGameInfo.m_Records.Count() );
  137. for ( int i = 0 ; i < m_SaveGameInfo.m_Records.Count(); ++i )
  138. {
  139. buf.Put( (const void *)&m_SaveGameInfo.m_Records[ i ], sizeof( Ep2LevelStats_t::SaveGameInfoRecord2_t ) );
  140. }
  141. CBaseGameStats::AppendLump( EP2_MAX_LUMP_COUNT, SaveBuffer, EP2STATS_LUMP_SAVEGAMEINFO, 1, buf.TellPut(), buf.Base() );
  142. }
  143. {
  144. CUtlBuffer buf;
  145. buf.PutShort( Ep2LevelStats_t::GenericStatsLump_t::LumpVersion );
  146. buf.PutInt( m_dictGeneric.Count() );
  147. for ( int i = m_dictGeneric.First(); i != m_dictGeneric.InvalidIndex(); i = m_dictGeneric.Next( i ) )
  148. {
  149. buf.PutString( m_dictGeneric.GetElementName( i ) );
  150. buf.Put( (const void *)&m_dictGeneric[ i ], sizeof( Ep2LevelStats_t::GenericStatsLump_t ) );
  151. }
  152. CBaseGameStats::AppendLump( EP2_MAX_LUMP_COUNT, SaveBuffer, EP2STATS_LUMP_GENERIC, 1, buf.TellPut(), buf.Base() );
  153. }
  154. }
  155. static void LoadData( CUtlDict<Ep2LevelStats_t, unsigned short>& items, CUtlBuffer &LoadBuffer )
  156. {
  157. // Read the next lump.
  158. unsigned short iLump = 0;
  159. unsigned short iLumpCount = 0;
  160. Ep2LevelStats_t *pItem = NULL;
  161. while( CBaseGameStats::GetLumpHeader( EP2_MAX_LUMP_COUNT, LoadBuffer, iLump, iLumpCount, true ) )
  162. {
  163. switch ( iLump )
  164. {
  165. case EP2STATS_LUMP_HEADER:
  166. {
  167. Ep2LevelStats_t::LevelHeader_t header;
  168. CBaseGameStats::LoadLump( LoadBuffer, iLumpCount, sizeof( Ep2LevelStats_t::LevelHeader_t ), &header );
  169. pItem = &items[ items.Insert( header.m_szMapName ) ];
  170. pItem->m_Header = header;
  171. pItem->m_Tag.Clear();
  172. Assert( pItem );
  173. }
  174. break;
  175. case EP2STATS_LUMP_TAG:
  176. {
  177. Assert( pItem );
  178. CBaseGameStats::LoadLump( LoadBuffer, iLumpCount, sizeof( Ep2LevelStats_t::Tag_t ), &pItem->m_Tag );
  179. }
  180. break;
  181. case EP2STATS_LUMP_DEATH:
  182. {
  183. Assert( pItem );
  184. pItem->m_aPlayerDeaths.SetCount( iLumpCount );
  185. CBaseGameStats::LoadLump( LoadBuffer, iLumpCount, sizeof( Ep2LevelStats_t::PlayerDeathsLump_t ), static_cast<void*>( pItem->m_aPlayerDeaths.Base() ) );
  186. }
  187. break;
  188. case EP2STATS_LUMP_NPC:
  189. {
  190. Assert( pItem );
  191. LoadBuffer.Get( ( void * )pItem->m_IntCounters, sizeof( pItem->m_IntCounters ) );
  192. LoadBuffer.Get( ( void * )pItem->m_FloatCounters, sizeof( pItem->m_FloatCounters ) );
  193. int c = LoadBuffer.GetInt();
  194. for ( int i = 0 ; i < c; ++i )
  195. {
  196. Ep2LevelStats_t::EntityDeathsLump_t data;
  197. char npcName[ 512 ];
  198. LoadBuffer.GetString( npcName );
  199. LoadBuffer.Get( &data, sizeof( data ) );
  200. pItem->m_dictEntityDeaths.Insert( npcName, data );
  201. }
  202. }
  203. break;
  204. case EP2STATS_LUMP_WEAPON:
  205. {
  206. Assert( pItem );
  207. int c = LoadBuffer.GetInt();
  208. for ( int i = 0 ; i < c; ++i )
  209. {
  210. Ep2LevelStats_t::WeaponLump_t data;
  211. char weaponName[ 512 ];
  212. LoadBuffer.GetString( weaponName );
  213. LoadBuffer.Get( &data, sizeof( data ) );
  214. pItem->m_dictWeapons.Insert( weaponName, data );
  215. }
  216. }
  217. break;
  218. case EP2STATS_LUMP_SAVEGAMEINFO:
  219. {
  220. Assert( pItem );
  221. Ep2LevelStats_t::SaveGameInfo_t *info = &pItem->m_SaveGameInfo;
  222. char sz[ 512 ];
  223. LoadBuffer.GetString( sz );
  224. info->m_sCurrentSaveFile = sz;
  225. info->m_nCurrentSaveFileTime = LoadBuffer.GetInt();
  226. int c = LoadBuffer.GetInt();
  227. for ( int i = 0 ; i < c; ++i )
  228. {
  229. Ep2LevelStats_t::SaveGameInfoRecord2_t rec;
  230. if ( pItem->m_Header.m_iVersion >= EP2_GAMESTATS_FILE_VERSION_02 )
  231. {
  232. LoadBuffer.Get( &rec, sizeof( rec ) );
  233. }
  234. else
  235. {
  236. size_t s = sizeof( Ep2LevelStats_t::SaveGameInfoRecord_t );
  237. LoadBuffer.Get( &rec, s );
  238. }
  239. info->m_Records.AddToTail( rec );
  240. }
  241. info->m_pCurrentRecord = NULL;
  242. if ( info->m_Records.Count() > 0 )
  243. {
  244. info->m_pCurrentRecord = &info->m_Records[ info->m_Records.Count() - 1 ];
  245. }
  246. }
  247. break;
  248. case EP2STATS_LUMP_GENERIC:
  249. {
  250. Assert( pItem );
  251. int version = LoadBuffer.GetShort();
  252. if ( version == Ep2LevelStats_t::GenericStatsLump_t::LumpVersion )
  253. {
  254. int c = LoadBuffer.GetInt();
  255. Assert( c < 2 * 1024 * 1024 );
  256. for ( int i = 0 ; i < c; ++i )
  257. {
  258. Ep2LevelStats_t::GenericStatsLump_t data;
  259. char pchStatName[ 512 ];
  260. LoadBuffer.GetString( pchStatName );
  261. LoadBuffer.Get( &data, sizeof( data ) );
  262. pItem->m_dictGeneric.Insert( pchStatName, data );
  263. }
  264. }
  265. else
  266. {
  267. Error( "Unsupported GenericStatsLump_t::LumpVersion" );
  268. }
  269. }
  270. break;
  271. }
  272. }
  273. }
  274. public:
  275. // Level header data.
  276. struct LevelHeader_t
  277. {
  278. static const unsigned short LumpId = EP2STATS_LUMP_HEADER; // Lump ids.
  279. byte m_iVersion; // Version of the game stats file.
  280. char m_szMapName[64]; // Name of the map.
  281. float m_flTime; // Time spent in level.
  282. };
  283. // Simple "tag" applied to all data in database (e.g., "PLAYTEST")
  284. struct Tag_t
  285. {
  286. static const unsigned short LumpId = EP2STATS_LUMP_TAG;
  287. void Clear()
  288. {
  289. Q_memset( m_szTagText, 0, sizeof( m_szTagText ) );
  290. m_nMapVersion = 0;
  291. }
  292. char m_szTagText[ 8 ];
  293. int m_nMapVersion;
  294. };
  295. // Player deaths.
  296. struct PlayerDeathsLump_t
  297. {
  298. static const unsigned short LumpId = EP2STATS_LUMP_DEATH; // Lump ids.
  299. short nPosition[3]; // Position of death.
  300. // short iWeapon; // Weapon that killed the player.
  301. // byte iAttackClass; // Class that killed the player.
  302. // byte iTargetClass; // Class of the player killed.
  303. };
  304. struct EntityDeathsLump_t
  305. {
  306. static const unsigned short LumpId = EP2STATS_LUMP_NPC;
  307. EntityDeathsLump_t() :
  308. m_nBodyCount( 0u ),
  309. m_nKilledPlayer( 0u )
  310. {
  311. }
  312. EntityDeathsLump_t( const EntityDeathsLump_t &other )
  313. {
  314. m_nBodyCount = other.m_nBodyCount;
  315. m_nKilledPlayer = other.m_nKilledPlayer;
  316. }
  317. unsigned int m_nBodyCount; // Number killed by player
  318. unsigned int m_nKilledPlayer; // Number of times entity killed player
  319. };
  320. struct WeaponLump_t
  321. {
  322. static const unsigned short LumpId = EP2STATS_LUMP_WEAPON;
  323. WeaponLump_t() :
  324. m_nShots( 0 ),
  325. m_nHits( 0 ),
  326. m_flDamageInflicted( 0.0 )
  327. {
  328. }
  329. WeaponLump_t( const WeaponLump_t &other )
  330. {
  331. m_nShots = other.m_nShots;
  332. m_nHits = other.m_nHits;
  333. m_flDamageInflicted = other.m_flDamageInflicted;
  334. }
  335. unsigned int m_nShots;
  336. unsigned int m_nHits;
  337. double m_flDamageInflicted;
  338. };
  339. struct SaveGameInfoRecord_t
  340. {
  341. SaveGameInfoRecord_t() :
  342. m_nFirstDeathIndex( -1 ),
  343. m_nNumDeaths( 0 ),
  344. m_nSaveHealth( -1 )
  345. {
  346. Q_memset( m_nSavePos, 0, sizeof( m_nSavePos ) );
  347. }
  348. int m_nFirstDeathIndex;
  349. int m_nNumDeaths;
  350. // Health and player pos from the save file
  351. short m_nSavePos[ 3 ];
  352. short m_nSaveHealth;
  353. };
  354. #pragma pack( 1 )
  355. // Adds save game type
  356. struct SaveGameInfoRecord2_t : public SaveGameInfoRecord_t
  357. {
  358. enum SaveType_t
  359. {
  360. TYPE_UNKNOWN = 0,
  361. TYPE_AUTOSAVE,
  362. TYPE_USERSAVE
  363. };
  364. SaveGameInfoRecord2_t() :
  365. m_SaveType( (byte)TYPE_UNKNOWN )
  366. {
  367. }
  368. byte m_SaveType;
  369. };
  370. #pragma pack()
  371. struct SaveGameInfo_t
  372. {
  373. static const unsigned short LumpId = EP2STATS_LUMP_SAVEGAMEINFO;
  374. SaveGameInfo_t() :
  375. m_nCurrentSaveFileTime( 0 ),
  376. m_pCurrentRecord( NULL )
  377. {
  378. }
  379. void Latch( char const *pchSaveName, unsigned int uFileTime )
  380. {
  381. m_pCurrentRecord = &m_Records[ m_Records.AddToTail() ];
  382. m_nCurrentSaveFileTime = uFileTime;
  383. m_sCurrentSaveFile = pchSaveName;
  384. }
  385. CUtlVector< SaveGameInfoRecord2_t > m_Records;
  386. SaveGameInfoRecord2_t *m_pCurrentRecord;
  387. unsigned int m_nCurrentSaveFileTime;
  388. CUtlString m_sCurrentSaveFile;
  389. };
  390. struct GenericStatsLump_t
  391. {
  392. static const unsigned short LumpId = EP2STATS_LUMP_GENERIC;
  393. static const unsigned short LumpVersion = 1;
  394. GenericStatsLump_t() :
  395. m_unCount( 0u ),
  396. m_flCurrentValue( 0.0 )
  397. {
  398. m_Pos[ 0 ] = m_Pos[ 1 ] = m_Pos[ 2 ] = 0;
  399. }
  400. short m_Pos[ 3 ];
  401. unsigned int m_unCount;
  402. double m_flCurrentValue;
  403. };
  404. // Data.
  405. LevelHeader_t m_Header; // Level header.
  406. Tag_t m_Tag;
  407. CUtlVector<PlayerDeathsLump_t> m_aPlayerDeaths; // List of player deaths.
  408. CUtlDict< EntityDeathsLump_t, int > m_dictEntityDeaths;
  409. CUtlDict< WeaponLump_t, int > m_dictWeapons;
  410. CUtlDict< GenericStatsLump_t, int > m_dictGeneric;
  411. SaveGameInfo_t m_SaveGameInfo;
  412. float m_FloatCounters[ NUM_FLOATCOUNTER_TYPES ];
  413. uint64 m_IntCounters[ NUM_INTCOUNTER_TYPES ];
  414. // Temporary data.
  415. bool m_bInitialized; // Has the map Map Stat Data been initialized.
  416. float m_flLevelStartTime;
  417. };
  418. #if defined( GAME_DLL )
  419. class CEP2GameStats : public CEP1GameStats
  420. {
  421. typedef CEP1GameStats BaseClass;
  422. public:
  423. CEP2GameStats();
  424. virtual ~CEP2GameStats();
  425. virtual CBaseGameStats *OnInit( CBaseGameStats *pCurrentGameStats, char const *gamedir ) { return pCurrentGameStats; }
  426. virtual bool UserPlayedAllTheMaps( void );
  427. virtual const char *GetStatSaveFileName( void );
  428. virtual const char *GetStatUploadRegistryKeyName( void );
  429. // Buffers.
  430. virtual void AppendCustomDataToSaveBuffer( CUtlBuffer &SaveBuffer );
  431. virtual void LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer );
  432. // Events
  433. virtual void Event_LevelInit( void );
  434. virtual void Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info );
  435. virtual void Event_PlayerDamage( CBasePlayer *pBasePlayer, const CTakeDamageInfo &info );
  436. virtual void Event_PlayerKilledOther( CBasePlayer *pAttacker, CBaseEntity *pVictim, const CTakeDamageInfo &info );
  437. virtual void Event_CrateSmashed();
  438. virtual void Event_Punted( CBaseEntity *pObject );
  439. virtual void Event_PlayerTraveled( CBasePlayer *pBasePlayer, float distanceInInches, bool bInVehicle, bool bSprinting );
  440. virtual void Event_WeaponFired( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName );
  441. virtual void Event_WeaponHit( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName, const CTakeDamageInfo &info );
  442. virtual void Event_SaveGame( void );
  443. virtual void Event_LoadGame( void );
  444. virtual void Event_FlippedVehicle( CBasePlayer *pDriver, CPropVehicleDriveable *pVehicle );
  445. // Called before .sav file is actually loaded (player should still be in previous level, if any)
  446. virtual void Event_PreSaveGameLoaded( char const *pSaveName, bool bInGame );
  447. virtual void Event_PlayerEnteredGodMode( CBasePlayer *pBasePlayer );
  448. virtual void Event_PlayerEnteredNoClip( CBasePlayer *pBasePlayer );
  449. virtual void Event_DecrementPlayerEnteredNoClip( CBasePlayer *pBasePlayer );
  450. // Generic statistics lump
  451. virtual void Event_IncrementCountedStatistic( const Vector& vecAbsOrigin, char const *pchStatisticName, float flIncrementAmount );
  452. public: //FIXME: temporary used for CC_ListDeaths command
  453. Ep2LevelStats_t *FindOrAddMapStats( const char *szMapName );
  454. public:
  455. Ep2LevelStats_t::EntityDeathsLump_t *FindDeathsLump( char const *npcName );
  456. Ep2LevelStats_t::WeaponLump_t *FindWeaponsLump( char const *pchWeaponName, bool bPrimary );
  457. Ep2LevelStats_t::GenericStatsLump_t *FindGenericLump( char const *pchStatName );
  458. // Utilities.
  459. Ep2LevelStats_t *GetCurrentMap( void ) { return m_pCurrentMap; }
  460. Ep2LevelStats_t *m_pCurrentMap;
  461. CUtlDict<Ep2LevelStats_t, unsigned short> m_dictMapStats;
  462. enum
  463. {
  464. INVEHICLE = 0,
  465. ONFOOT,
  466. ONFOOTSPRINTING,
  467. NUM_TRAVEL_TYPES
  468. };
  469. float m_flInchesRemainder[ NUM_TRAVEL_TYPES ];
  470. };
  471. #endif
  472. #endif // EP2_GAMESTATS_H