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.

584 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #if defined( GAME_DLL )
  7. #include "cbase.h"
  8. #endif
  9. #include "ep2_gamestats.h"
  10. #include "tier1/utlbuffer.h"
  11. #include "vehicle_base.h"
  12. #include "tier1/utlstring.h"
  13. #include "filesystem.h"
  14. #include "icommandline.h"
  15. static CEP2GameStats s_CEP2GameStats_Singleton;
  16. CBaseGameStats *g_pEP2GameStats = &s_CEP2GameStats_Singleton;
  17. CEP2GameStats::CEP2GameStats( void )
  18. {
  19. Q_memset( m_flInchesRemainder, 0, sizeof( m_flInchesRemainder ) );
  20. m_pCurrentMap = NULL;
  21. m_dictMapStats.Purge();
  22. }
  23. const char *CEP2GameStats::GetStatSaveFileName( void )
  24. {
  25. //overriding the default for backwards compatibility with release stat tracking code
  26. return "ep2_gamestats.dat";
  27. }
  28. const char *CEP2GameStats::GetStatUploadRegistryKeyName( void )
  29. {
  30. //overriding the default for backwards compatibility with release stat tracking code
  31. return "GameStatsUpload_Ep2";
  32. }
  33. static char const *ep2Maps[] =
  34. {
  35. "ep2_outland_01",
  36. "ep2_outland_02",
  37. "ep2_outland_03",
  38. "ep2_outland_04",
  39. "ep2_outland_05",
  40. "ep2_outland_06",
  41. "ep2_outland_06a",
  42. "ep2_outland_07",
  43. "ep2_outland_08",
  44. "ep2_outland_09",
  45. "ep2_outland_10",
  46. "ep2_outland_10a",
  47. "ep2_outland_11",
  48. "ep2_outland_11a",
  49. "ep2_outland_12",
  50. "ep2_outland_12a"
  51. };
  52. bool CEP2GameStats::UserPlayedAllTheMaps( void )
  53. {
  54. int c = ARRAYSIZE( ep2Maps );
  55. for ( int i = 0; i < c; ++i )
  56. {
  57. int idx = m_BasicStats.m_MapTotals.Find( ep2Maps[ i ] );
  58. if( idx == m_BasicStats.m_MapTotals.InvalidIndex() )
  59. return false;
  60. }
  61. return true;
  62. }
  63. //-----------------------------------------------------------------------------
  64. // Purpose: Destructor
  65. // Input : -
  66. //-----------------------------------------------------------------------------
  67. CEP2GameStats::~CEP2GameStats()
  68. {
  69. m_pCurrentMap = NULL;
  70. m_dictMapStats.Purge();
  71. }
  72. //-----------------------------------------------------------------------------
  73. // Purpose:
  74. // Input : &SaveBuffer -
  75. //-----------------------------------------------------------------------------
  76. void CEP2GameStats::AppendCustomDataToSaveBuffer( CUtlBuffer &SaveBuffer )
  77. {
  78. // Save data per map.
  79. for ( int iMap = m_dictMapStats.First(); iMap != m_dictMapStats.InvalidIndex(); iMap = m_dictMapStats.Next( iMap ) )
  80. {
  81. // Get the current map.
  82. Ep2LevelStats_t *pCurrentMap = &m_dictMapStats[iMap];
  83. Assert( pCurrentMap );
  84. pCurrentMap->AppendToBuffer( SaveBuffer );
  85. }
  86. }
  87. void CEP2GameStats::LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer )
  88. {
  89. Ep2LevelStats_t::LoadData( m_dictMapStats, LoadBuffer );
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Purpose:
  93. //-----------------------------------------------------------------------------
  94. void CEP2GameStats::Event_LevelInit( void )
  95. {
  96. BaseClass::Event_LevelInit();
  97. char const *pchTag = NULL;
  98. CommandLine()->CheckParm( "-gamestatstag", &pchTag );
  99. if ( !pchTag )
  100. {
  101. pchTag = "";
  102. }
  103. m_pCurrentMap = FindOrAddMapStats( STRING( gpGlobals->mapname ) );
  104. m_pCurrentMap->Init( STRING( gpGlobals->mapname ), gpGlobals->curtime, pchTag, gpGlobals->mapversion );
  105. }
  106. Ep2LevelStats_t::EntityDeathsLump_t *CEP2GameStats::FindDeathsLump( char const *npcName )
  107. {
  108. if ( !m_pCurrentMap )
  109. return NULL;
  110. char const *name = npcName;
  111. // Hack to fixup name
  112. if ( !Q_stricmp( name, "npc_ministrider" ) )
  113. {
  114. name = "npc_hunter";
  115. }
  116. if ( Q_strnicmp( name, "npc_", 4 ) )
  117. return NULL;
  118. int idx = m_pCurrentMap->m_dictEntityDeaths.Find( name );
  119. if ( idx == m_pCurrentMap->m_dictEntityDeaths.InvalidIndex() )
  120. {
  121. idx = m_pCurrentMap->m_dictEntityDeaths.Insert( name );
  122. }
  123. return &m_pCurrentMap->m_dictEntityDeaths[ idx ];
  124. }
  125. Ep2LevelStats_t::WeaponLump_t *CEP2GameStats::FindWeaponsLump( char const *pchWeaponName, bool bPrimary )
  126. {
  127. if ( !m_pCurrentMap )
  128. return NULL;
  129. if ( !pchWeaponName )
  130. {
  131. AssertOnce( !"FindWeaponsLump pchWeaponName == NULL" );
  132. return NULL;
  133. }
  134. char lookup[ 512 ];
  135. Q_snprintf( lookup, sizeof( lookup ), "%s_%s", pchWeaponName, bPrimary ? "primary" : "secondary" );
  136. int idx = m_pCurrentMap->m_dictWeapons.Find( lookup );
  137. if ( idx == m_pCurrentMap->m_dictWeapons.InvalidIndex() )
  138. {
  139. idx = m_pCurrentMap->m_dictWeapons.Insert( lookup );
  140. }
  141. return &m_pCurrentMap->m_dictWeapons[ idx ];
  142. }
  143. // Finds the generic stats lump
  144. Ep2LevelStats_t::GenericStatsLump_t *CEP2GameStats::FindGenericLump( char const *pchStatName )
  145. {
  146. if ( !m_pCurrentMap )
  147. return NULL;
  148. if ( !pchStatName || !*pchStatName )
  149. return NULL;
  150. int idx = m_pCurrentMap->m_dictGeneric.Find( pchStatName );
  151. if ( idx == m_pCurrentMap->m_dictGeneric.InvalidIndex() )
  152. {
  153. idx = m_pCurrentMap->m_dictGeneric.Insert( pchStatName );
  154. }
  155. return &m_pCurrentMap->m_dictGeneric[ idx ];
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Purpose:
  159. // Input : *szMapName -
  160. // Output : Ep2LevelStats_t
  161. //-----------------------------------------------------------------------------
  162. Ep2LevelStats_t *CEP2GameStats::FindOrAddMapStats( const char *szMapName )
  163. {
  164. int iMap = m_dictMapStats.Find( szMapName );
  165. if( iMap == m_dictMapStats.InvalidIndex() )
  166. {
  167. iMap = m_dictMapStats.Insert( szMapName );
  168. }
  169. return &m_dictMapStats[iMap];
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose:
  173. //-----------------------------------------------------------------------------
  174. void CEP2GameStats::Event_PlayerDamage( CBasePlayer *pBasePlayer, const CTakeDamageInfo &info )
  175. {
  176. BaseClass::Event_PlayerDamage( pBasePlayer, info );
  177. m_pCurrentMap->m_FloatCounters[ Ep2LevelStats_t::COUNTER_DAMAGETAKEN ] += info.GetDamage();
  178. }
  179. //-----------------------------------------------------------------------------
  180. // Purpose:
  181. //-----------------------------------------------------------------------------
  182. void CEP2GameStats::Event_PlayerKilledOther( CBasePlayer *pAttacker, CBaseEntity *pVictim, const CTakeDamageInfo &info )
  183. {
  184. BaseClass::Event_PlayerKilledOther( pAttacker, pVictim, info );
  185. if ( pAttacker )
  186. {
  187. StatsLog( "Attacker: %s\n", pAttacker->GetClassname() );
  188. }
  189. if ( !pVictim )
  190. {
  191. return;
  192. }
  193. char const *pchVictim = pVictim->GetClassname();
  194. Ep2LevelStats_t::EntityDeathsLump_t *lump = FindDeathsLump( pchVictim );
  195. if ( lump )
  196. {
  197. ++lump->m_nBodyCount;
  198. StatsLog( "Player has killed %d %s's\n", lump->m_nBodyCount, pchVictim );
  199. CPropVehicleDriveable *veh = dynamic_cast< CPropVehicleDriveable * >( pAttacker );
  200. if ( !veh )
  201. veh = dynamic_cast< CPropVehicleDriveable * >( info.GetInflictor() );
  202. if ( veh )
  203. {
  204. CBaseEntity *driver = veh->GetDriver();
  205. if ( driver && driver->IsPlayer() )
  206. {
  207. ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_VEHICULARHOMICIDES ];
  208. StatsLog( " Vehicular homicide [%I64d] of %s's\n", m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_VEHICULARHOMICIDES ], pchVictim );
  209. }
  210. }
  211. }
  212. else
  213. {
  214. StatsLog( "Player killed %s (not tracked)\n", pchVictim );
  215. }
  216. }
  217. void CEP2GameStats::Event_Punted( CBaseEntity *pObject )
  218. {
  219. BaseClass::Event_Punted( pObject );
  220. ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_OBJECTSPUNTED ];
  221. }
  222. //-----------------------------------------------------------------------------
  223. // Purpose:
  224. //-----------------------------------------------------------------------------
  225. void CEP2GameStats::Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info )
  226. {
  227. BaseClass::Event_PlayerKilled( pPlayer, info );
  228. if ( info.GetDamageType() & DMG_FALL )
  229. {
  230. ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_FALLINGDEATHS ];
  231. }
  232. Ep2LevelStats_t::PlayerDeathsLump_t death;
  233. // set the location where the target died
  234. const Vector &org = pPlayer->GetAbsOrigin();
  235. death.nPosition[ 0 ] = static_cast<short>( org.x );
  236. death.nPosition[ 1 ] = static_cast<short>( org.y );
  237. death.nPosition[ 2 ] = static_cast<short>( org.z );
  238. StatsLog( "CEP2GameStats::Event_PlayerKilled at location [%d %d %d]\n", (int)death.nPosition[ 0 ], (int)death.nPosition[ 1 ], (int)death.nPosition[ 2 ] );
  239. // set the class of the attacker
  240. CBaseEntity *pInflictor = info.GetInflictor();
  241. CBaseEntity *pKiller = info.GetAttacker();
  242. if ( pInflictor )
  243. {
  244. StatsLog( "Inflictor: %s\n", pInflictor->GetClassname() );
  245. }
  246. if ( pKiller )
  247. {
  248. char const *pchKiller = pKiller->GetClassname();
  249. Ep2LevelStats_t::EntityDeathsLump_t *lump = FindDeathsLump( pchKiller );
  250. if ( lump )
  251. {
  252. ++lump->m_nKilledPlayer;
  253. StatsLog( "Player has been killed %d times by %s's\n", lump->m_nKilledPlayer, pchKiller );
  254. }
  255. else
  256. {
  257. StatsLog( "Player killed by %s (not tracked)\n", pchKiller );
  258. }
  259. }
  260. // add it to the list of deaths
  261. Ep2LevelStats_t *map = FindOrAddMapStats( STRING( gpGlobals->mapname ) );
  262. int slot = map->m_aPlayerDeaths.AddToTail( death );
  263. Ep2LevelStats_t::SaveGameInfoRecord2_t *rec = map->m_SaveGameInfo.m_pCurrentRecord;
  264. if ( rec )
  265. {
  266. if ( rec->m_nFirstDeathIndex == -1 )
  267. {
  268. rec->m_nFirstDeathIndex = slot;
  269. }
  270. ++rec->m_nNumDeaths;
  271. StatsLog( "Player has died %d times since last save/load\n", rec->m_nNumDeaths );
  272. }
  273. }
  274. void CEP2GameStats::Event_CrateSmashed()
  275. {
  276. BaseClass::Event_CrateSmashed();
  277. ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_CRATESSMASHED ];
  278. }
  279. void CEP2GameStats::Event_PlayerTraveled( CBasePlayer *pBasePlayer, float distanceInInches, bool bInVehicle, bool bSprinting )
  280. {
  281. BaseClass::Event_PlayerTraveled( pBasePlayer, distanceInInches, bInVehicle, bSprinting );
  282. int iIndex = INVEHICLE;
  283. if ( !bInVehicle )
  284. {
  285. iIndex = bSprinting ? ONFOOTSPRINTING : ONFOOT;
  286. }
  287. m_flInchesRemainder[ iIndex ] += distanceInInches;
  288. uint64 intPart = (uint64)m_flInchesRemainder[ iIndex ];
  289. m_flInchesRemainder[ iIndex ] -= intPart;
  290. if ( intPart > 0 )
  291. {
  292. if ( bInVehicle )
  293. {
  294. m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_DISTANCE_INVEHICLE ] += intPart;
  295. }
  296. else
  297. {
  298. if ( bSprinting )
  299. {
  300. m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_DISTANCE_ONFOOTSPRINTING ] += intPart;
  301. }
  302. else
  303. {
  304. m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_DISTANCE_ONFOOT ] += intPart;
  305. }
  306. }
  307. }
  308. Ep2LevelStats_t *map = m_pCurrentMap;
  309. if ( !map )
  310. return;
  311. Ep2LevelStats_t::SaveGameInfoRecord2_t *rec = map->m_SaveGameInfo.m_pCurrentRecord;
  312. if ( rec &&
  313. rec->m_nSaveHealth == -1 )
  314. {
  315. Vector pos = pBasePlayer->GetAbsOrigin();
  316. rec->m_nSavePos[ 0 ] = (short)pos.x;
  317. rec->m_nSavePos[ 1 ] = (short)pos.y;
  318. rec->m_nSavePos[ 2 ] = (short)pos.z;
  319. rec->m_nSaveHealth = clamp( pBasePlayer->GetHealth(), 0, 100 );
  320. }
  321. }
  322. void CEP2GameStats::Event_WeaponFired( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName )
  323. {
  324. BaseClass::Event_WeaponFired( pShooter, bPrimary, pchWeaponName );
  325. Ep2LevelStats_t::WeaponLump_t *lump = FindWeaponsLump( pchWeaponName, bPrimary );
  326. if ( lump )
  327. {
  328. ++lump->m_nShots;
  329. }
  330. }
  331. void CEP2GameStats::Event_WeaponHit( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName, const CTakeDamageInfo &info )
  332. {
  333. BaseClass::Event_WeaponHit( pShooter, bPrimary, pchWeaponName, info );
  334. Ep2LevelStats_t::WeaponLump_t *lump = FindWeaponsLump( pchWeaponName, bPrimary );
  335. if ( lump )
  336. {
  337. ++lump->m_nHits;
  338. lump->m_flDamageInflicted += info.GetDamage();
  339. }
  340. }
  341. void CEP2GameStats::Event_SaveGame( void )
  342. {
  343. BaseClass::Event_SaveGame();
  344. Ep2LevelStats_t *map = m_pCurrentMap;
  345. if ( !map )
  346. return;
  347. ++map->m_IntCounters[ Ep2LevelStats_t::COUNTER_SAVES ];
  348. StatsLog( " %I64uth save on this map\n", map->m_IntCounters[ Ep2LevelStats_t::COUNTER_SAVES ] );
  349. char const *pchSaveFile = engine->GetSaveFileName();
  350. if ( !pchSaveFile || !pchSaveFile[ 0 ] )
  351. return;
  352. char name[ 512 ];
  353. Q_strncpy( name, pchSaveFile, sizeof( name ) );
  354. Q_strlower( name );
  355. Q_FixSlashes( name );
  356. unsigned int uFileTime = filesystem->GetFileTime( name, "GAME" );
  357. // Latch off previous
  358. map->m_SaveGameInfo.Latch( name, uFileTime );
  359. Ep2LevelStats_t::SaveGameInfoRecord2_t *rec = map->m_SaveGameInfo.m_pCurrentRecord;
  360. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  361. if ( pPlayer )
  362. {
  363. Vector pos = pPlayer->GetAbsOrigin();
  364. rec->m_nSavePos[ 0 ] = (short)pos.x;
  365. rec->m_nSavePos[ 1 ] = (short)pos.y;
  366. rec->m_nSavePos[ 2 ] = (short)pos.z;
  367. rec->m_nSaveHealth = clamp( pPlayer->GetHealth(), 0, 100 );
  368. rec->m_SaveType = Q_stristr( pchSaveFile, "autosave" ) ?
  369. Ep2LevelStats_t::SaveGameInfoRecord2_t::TYPE_AUTOSAVE : Ep2LevelStats_t::SaveGameInfoRecord2_t::TYPE_USERSAVE;
  370. StatsLog( "save pos %i %i %i w/ health %d\n",
  371. rec->m_nSavePos[ 0 ],
  372. rec->m_nSavePos[ 1 ],
  373. rec->m_nSavePos[ 2 ],
  374. rec->m_nSaveHealth );
  375. }
  376. }
  377. void CEP2GameStats::Event_LoadGame( void )
  378. {
  379. BaseClass::Event_LoadGame();
  380. Ep2LevelStats_t *map = m_pCurrentMap;
  381. if ( !map )
  382. return;
  383. ++map->m_IntCounters[ Ep2LevelStats_t::COUNTER_LOADS ];
  384. StatsLog( " %I64uth load on this map\n", map->m_IntCounters[ Ep2LevelStats_t::COUNTER_LOADS ] );
  385. char const *pchSaveFile = engine->GetMostRecentlyLoadedFileName();
  386. if ( !pchSaveFile || !pchSaveFile[ 0 ] )
  387. return;
  388. char name[ 512 ];
  389. Q_snprintf( name, sizeof( name ), "save/%s", pchSaveFile );
  390. Q_DefaultExtension( name, IsX360() ? ".360.sav" : ".sav", sizeof( name ) );
  391. Q_FixSlashes( name );
  392. Q_strlower( name );
  393. Ep2LevelStats_t::SaveGameInfo_t *pSaveGameInfo = &map->m_SaveGameInfo;
  394. if ( pSaveGameInfo->m_nCurrentSaveFileTime == 0 ||
  395. pSaveGameInfo->m_sCurrentSaveFile != name )
  396. {
  397. unsigned int uFileTime = filesystem->GetFileTime( name, "GAME" );
  398. // Latch off previous
  399. StatsLog( "Relatching save game file due to time or filename change (%s : %u)\n", name, uFileTime );
  400. pSaveGameInfo->Latch( name, uFileTime );
  401. }
  402. }
  403. void CEP2GameStats::Event_FlippedVehicle( CBasePlayer *pDriver, CPropVehicleDriveable *pVehicle )
  404. {
  405. BaseClass::Event_FlippedVehicle( pDriver, pVehicle );
  406. ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_VEHICLE_OVERTURNED ];
  407. StatsLog( "%I64u time vehicle overturned\n", m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_VEHICLE_OVERTURNED ] );
  408. }
  409. void CEP2GameStats::Event_PreSaveGameLoaded( char const *pSaveName, bool bInGame )
  410. {
  411. BaseClass::Event_PreSaveGameLoaded( pSaveName, bInGame );
  412. // Not currently in a level
  413. if ( !bInGame )
  414. return;
  415. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  416. if ( !pPlayer )
  417. return;
  418. // We're loading a saved game while the player is still alive (are they stuck?)
  419. if ( pPlayer->IsAlive() )
  420. {
  421. ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_LOADGAME_STILLALIVE ];
  422. StatsLog( "%I64u game loaded with living player\n", m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_LOADGAME_STILLALIVE ] );
  423. }
  424. }
  425. void CEP2GameStats::Event_PlayerEnteredGodMode( CBasePlayer *pBasePlayer )
  426. {
  427. BaseClass::Event_PlayerEnteredGodMode( pBasePlayer );
  428. ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_GODMODES ];
  429. StatsLog( "%I64u time entering godmode\n", m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_GODMODES ] );
  430. }
  431. void CEP2GameStats::Event_PlayerEnteredNoClip( CBasePlayer *pBasePlayer )
  432. {
  433. BaseClass::Event_PlayerEnteredNoClip( pBasePlayer );
  434. ++m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_NOCLIPS ];
  435. StatsLog( "%I64u time entering NOCLIP\n", m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_NOCLIPS ] );
  436. }
  437. void CEP2GameStats::Event_DecrementPlayerEnteredNoClip( CBasePlayer *pBasePlayer )
  438. {
  439. BaseClass::Event_DecrementPlayerEnteredNoClip( pBasePlayer );
  440. if ( m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_NOCLIPS ] > 0 )
  441. {
  442. --m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_NOCLIPS ];
  443. }
  444. StatsLog( "%I64u decrement entering NOCLIP (entering vehicle doesn't count)\n", m_pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_NOCLIPS ] );
  445. }
  446. // Generic statistics lump
  447. void CEP2GameStats::Event_IncrementCountedStatistic( const Vector& vecAbsOrigin, char const *pchStatisticName, float flIncrementAmount )
  448. {
  449. BaseClass::Event_IncrementCountedStatistic( vecAbsOrigin, pchStatisticName, flIncrementAmount );
  450. // Find the generic lump
  451. Ep2LevelStats_t::GenericStatsLump_t *lump = FindGenericLump( pchStatisticName );
  452. if ( lump )
  453. {
  454. lump->m_Pos[ 0 ] = (short)vecAbsOrigin.x;
  455. lump->m_Pos[ 1 ] = (short)vecAbsOrigin.y;
  456. lump->m_Pos[ 2 ] = (short)vecAbsOrigin.z;
  457. lump->m_flCurrentValue += (double)flIncrementAmount;
  458. ++lump->m_unCount;
  459. }
  460. }
  461. //-----------------------------------------------------------------------------
  462. // Purpose:
  463. //-----------------------------------------------------------------------------
  464. static void CC_ListDeaths( const CCommand &args )
  465. {
  466. Ep2LevelStats_t *map = s_CEP2GameStats_Singleton.FindOrAddMapStats( STRING( gpGlobals->mapname ) );
  467. if ( !map )
  468. return;
  469. int nRendered = 0;
  470. for ( int i = map->m_aPlayerDeaths.Count() - 1; i >= 0 ; --i, ++nRendered )
  471. {
  472. Vector org( map->m_aPlayerDeaths[ i ].nPosition[ 0 ],
  473. map->m_aPlayerDeaths[ i ].nPosition[ 1 ],
  474. map->m_aPlayerDeaths[ i ].nPosition[ 2 ] + 36.0f );
  475. // FIXME: This might overflow
  476. NDebugOverlay::Box( org, Vector( -8, -8, -8 ), Vector( 8, 8, 8 ), 0, 255, 0, 128, 10.0f );
  477. /*
  478. Msg( "%s killed %s with %s at (%d,%d,%d)\n",
  479. g_aClassNames[ map->m_aPlayerDeaths[ i ].iAttackClass ],
  480. g_aClassNames[ map->m_aPlayerDeaths[ i ].iTargetClass ],
  481. WeaponIdToAlias( map->m_aPlayerDeaths[ i ].iWeapon ),
  482. map->m_aPlayerDeaths[ i ].nPosition[ 0 ],
  483. map->m_aPlayerDeaths[ i ].nPosition[ 1 ],
  484. map->m_aPlayerDeaths[ i ].nPosition[ 2 ] );
  485. */
  486. if ( nRendered > 150 )
  487. break;
  488. }
  489. Msg( "\nlisted %d deaths\n", map->m_aPlayerDeaths.Count() );
  490. }
  491. static ConCommand listDeaths("listdeaths", CC_ListDeaths, "lists player deaths", 0 );