Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1236 lines
45 KiB

  1. #include "cbase.h"
  2. #include "cs_gamerules.h"
  3. #include "cs_gamestats.h"
  4. #include "funfactmgr_cs.h"
  5. #include "funfact_cs.h"
  6. #include "weapon_csbase.h"
  7. #include "cs_achievement_constants.h"
  8. const float kFirstBloodTimeClassic = 30.0f;
  9. const float kFirstKillTimeClassic = 45.0f;
  10. const float kFirstBloodTimeDemolition = 15.0f;
  11. const float kFirstKillTimeDemolition = 25.0f;
  12. const float kFastRoundTimeClassic = 40.0f;
  13. const float kFastRoundTimeElimination = 30.0f; // used for demolition fast win fun facts
  14. const int kMinShotsForAccuracy = 15; // number of shots required for accuracy to be considered valid
  15. const float kAccuracyAdvantage = 0.25f; // delta required for player > team
  16. const float kMinTeamAccuracy = 0.30f; // accuracy required for team fun facts
  17. enum FunFactId
  18. {
  19. FF_CT_WIN_NO_KILLS,
  20. FF_T_WIN_NO_KILLS,
  21. FF_KILL_DEFUSER,
  22. FF_KILL_RESCUER,
  23. FF_T_WIN_NO_CASUALTIES,
  24. FF_CT_WIN_NO_CASUALTIES,
  25. FF_DAMAGE_WITH_GRENADES,
  26. FF_KILLS_WITH_GRENADES,
  27. FF_SINGLE_GRENADE_KILLS,
  28. FF_DAMAGE_NO_KILLS,
  29. FF_KILLED_ENEMIES,
  30. FF_FIRST_KILL,
  31. FF_FIRST_BLOOD,
  32. FF_SHORT_ROUND,
  33. FF_BEST_ACCURACY,
  34. FF_KNIFE_KILLS,
  35. FF_BLIND_KILLS,
  36. FF_KILLS_WITH_LAST_ROUND,
  37. FF_DONATED_WEAPONS,
  38. FF_POSTHUMOUS_GRENADE_KILLS,
  39. FF_KNIFE_IN_GUNFIGHT,
  40. FF_NUM_TIMES_JUMPED,
  41. FF_FALL_DAMAGE,
  42. FF_ITEMS_PURCHASED,
  43. FF_WON_AS_LAST_MEMBER,
  44. FF_NUMBER_OF_OVERKILLS,
  45. FF_SHOTS_FIRED,
  46. FF_SHOTS_FIRED_GG,
  47. FF_MONEY_SPENT,
  48. FF_MULTIPLE_ATTACKS_LIVED,
  49. FF_MULTIPLE_ATTACKS_DIED,
  50. FF_DAMAGE_MULTIPLE_ENEMIES,
  51. FF_GRENADES_THROWN,
  52. FF_USED_ALL_AMMO,
  53. FF_DEFENDED_BOMB,
  54. FF_ITEMS_DROPPED_VALUE,
  55. FF_KILL_WOUNDED_ENEMIES,
  56. FF_USED_MULTIPLE_WEAPONS,
  57. FF_T_ACCURACY,
  58. FF_CT_ACCURACY,
  59. FF_BEST_T_ACCURACY,
  60. FF_BEST_CT_ACCURACY,
  61. FF_KILLS_HEADSHOTS,
  62. FF_KILLS_WITH_STATTRAK_WEAPON,
  63. FF_BROKE_WINDOWS,
  64. FF_NIGHTVISION_DAMAGE,
  65. FF_DEFUSED_WITH_DROPPED_KIT,
  66. FF_KILLED_HALF_OF_ENEMIES,
  67. FF_KILLED_ALL_ENEMIES,
  68. FF_KNIFE_LEVEL_REACHED,
  69. FF_MAX_KILLED_BEFORE_DYING,
  70. FF_MAX_RESPAWNS,
  71. FF_DEFAULT_WEAPON,
  72. FF_ROUNDS_WITHOUT_DYING,
  73. FF_TASER_KILL,
  74. FF_TICKING_TIME,
  75. FF_CT_WIN_TIME,
  76. FF_TER_WIN_TIME,
  77. FF_BOTS_ASSUMED,
  78. FF_DOMINATION,
  79. FF_REVENGE,
  80. FF_STEPS_TAKEN,
  81. FF_QUARTER_HEALTH,
  82. FF_EMPTY_GUNS,
  83. FF_SLOW_TRIGGER,
  84. FF_PICKUP_BOMB,
  85. FF_BOMB_CARRIERS,
  86. FF_KNIFE_BOMB_PLANTER,
  87. FF_BOMB_PLANTED_BEFORE_KILL,
  88. FF_FAILED_BOMB_PLANTS,
  89. FF_KNIFE_WITHOUT_AMMO,
  90. FF_MOLOTOV_BURNS,
  91. FF_SURVIVAL_TIME,
  92. FF_PULLED_TRIGGER,
  93. FF_DEFUSE_WAS_CLOSE_CALL_TENTHS,
  94. FF_DEFUSE_WAS_CLOSE_CALL_HUNDREDTHS,
  95. FF_DEFUSE_WAS_CLOSE_CALL_THOUSANDTHS,
  96. FF_FALLBACK,
  97. };
  98. CFunFactHelper *CFunFactHelper::s_pFirst = NULL;
  99. //=============================================================================
  100. // Per-player evaluation Fun Fact
  101. // Evaluate the function per player and generate a fun fact for each valid or
  102. // highest valid player
  103. //=============================================================================
  104. namespace GameFlags
  105. {
  106. enum Type
  107. {
  108. None = 0x0000,
  109. Classic = 0x0001,
  110. Demolition = 0x0002,
  111. GunGame = 0x0004,
  112. AllModes = 0x0007,
  113. NotGunGame = AllModes & ~GunGame,
  114. Elimination = 0x0100, // win by elimination
  115. };
  116. };
  117. bool GameQualifies( e_RoundEndReason roundResult, int gameFlags )
  118. {
  119. if ( gameFlags & GameFlags::Elimination )
  120. {
  121. if ( roundResult != CTs_Win && roundResult != Terrorists_Win )
  122. return false;
  123. }
  124. // check to see if we're enabled to run in our game mode
  125. if ( (gameFlags & GameFlags::Classic) && CSGameRules()->IsPlayingClassic() )
  126. return true;
  127. if ( (gameFlags & GameFlags::Demolition) && CSGameRules()->IsPlayingGunGameTRBomb() )
  128. return true;
  129. if ( (gameFlags & GameFlags::GunGame) && CSGameRules()->IsPlayingGunGameProgressive() )
  130. return true;
  131. return false;
  132. }
  133. //=============================================================================
  134. // Generic evaluation Fun Fact
  135. // This fun fact will evaluate the specified function to determine when it is
  136. // valid. This is basically just a glue class for simple evaluation functions.
  137. //=============================================================================
  138. // Function type that we use to evaluate our fun facts. The data is returned as ints then floats that are passed in as reference parameters
  139. typedef bool (*fFunFactEval)( int &iPlayer, int &data1, int &data2, int &data3 );
  140. class CFunFact_GenericEvalFunction : public FunFactEvaluator
  141. {
  142. public:
  143. CFunFact_GenericEvalFunction(FunFactId id, const char* szLocalizationToken, float fCoolness, fFunFactEval pfnEval, int gameFlags ) :
  144. FunFactEvaluator(id, szLocalizationToken, fCoolness),
  145. m_pfnEval(pfnEval),
  146. m_gameFlags(gameFlags)
  147. {}
  148. virtual bool Evaluate( e_RoundEndReason roundResult, FunFactVector& results ) const
  149. {
  150. if ( !GameQualifies(roundResult, m_gameFlags) )
  151. return false;
  152. FunFact funfact;
  153. if (m_pfnEval(funfact.iPlayer, funfact.iData1, funfact.iData2, funfact.iData3))
  154. {
  155. funfact.id = GetId();
  156. funfact.szLocalizationToken = GetLocalizationToken();
  157. funfact.fMagnitude = 0.0f;
  158. results.AddToTail(funfact);
  159. return true;
  160. }
  161. else
  162. return false;
  163. }
  164. private:
  165. fFunFactEval m_pfnEval;
  166. int m_gameFlags;
  167. };
  168. #define DECLARE_FUNFACT_EVALFUNC(funfactId, szLocalizationToken, fCoolness, pfnEval, gameFlags) \
  169. static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
  170. { \
  171. return new CFunFact_GenericEvalFunction(funfactId, szLocalizationToken, fCoolness, pfnEval, gameFlags); \
  172. }; \
  173. static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
  174. //=============================================================================
  175. // Per-player evaluation Fun Fact
  176. // Evaluate the function per player and generate a fun fact for each valid or
  177. // highest valid player
  178. //=============================================================================
  179. namespace PlayerFlags
  180. {
  181. enum Type
  182. {
  183. All = 0x0000,
  184. TeamCT = 0x0001,
  185. TeamTerrorist = 0x0002,
  186. HighestOnly = 0x0004, // when not set, generates fun facts for all valid testees
  187. Alive = 0x0008,
  188. Dead = 0x0010,
  189. WinningTeam = 0x0020,
  190. LosingTeam = 0x0040,
  191. HasKilledThisRound = 0x0080,
  192. AllowIfControlledBot = 0x0100, // allow if this player controlled a bot this round
  193. };
  194. };
  195. bool PlayerQualifies( CBasePlayer* pPlayer, int playerFlags )
  196. {
  197. if ( (playerFlags & PlayerFlags::TeamCT) && pPlayer->GetTeamNumber() != TEAM_CT )
  198. return false;
  199. if ( (playerFlags & PlayerFlags::TeamTerrorist) && pPlayer->GetTeamNumber() != TEAM_TERRORIST )
  200. return false;
  201. if ( !pPlayer )
  202. return false;
  203. CCSPlayer* pCSPlayer = ToCSPlayer(pPlayer);
  204. if( pCSPlayer && pCSPlayer->IsBot() && pCSPlayer->HasBeenControlledThisRound() )
  205. return false; // bots are not allowed to be picked after being controlled
  206. if ( !( playerFlags & PlayerFlags::AllowIfControlledBot ) && pCSPlayer && pCSPlayer->HasControlledBotThisRound() )
  207. return false;
  208. if( pPlayer->GetTeamNumber() == TEAM_SPECTATOR )
  209. return false;
  210. if ( (playerFlags & PlayerFlags::Dead) && const_cast<CBasePlayer*>(pPlayer)->IsAlive() ) // IsAlive() really isn't const correct
  211. return false;
  212. if ( (playerFlags & PlayerFlags::Alive) && !const_cast<CBasePlayer*>(pPlayer)->IsAlive() )
  213. return false;
  214. if ( (playerFlags & PlayerFlags::WinningTeam) && pPlayer->GetTeamNumber() != CSGameRules()->m_iRoundWinStatus )
  215. return false;
  216. if ( (playerFlags & PlayerFlags::LosingTeam) && pPlayer->GetTeamNumber() == CSGameRules()->m_iRoundWinStatus )
  217. return false;
  218. if ( ( playerFlags & PlayerFlags::HasKilledThisRound ) && CCS_GameStats.FindPlayerStats( pPlayer ).statsCurrentRound[CSSTAT_KILLS] == 0 )
  219. return false;
  220. return true;
  221. }
  222. typedef int (*PlayerEvalFunction)(CCSPlayer* pPlayer);
  223. class CFunFact_PlayerEvalFunction : public FunFactEvaluator
  224. {
  225. public:
  226. CFunFact_PlayerEvalFunction(FunFactId id, const char* szLocalizationToken, float fCoolness, PlayerEvalFunction pfnEval,
  227. int iMin, int gameFlags, int playerFlags ) :
  228. FunFactEvaluator(id, szLocalizationToken, fCoolness),
  229. m_pfnEval(pfnEval),
  230. m_min(iMin),
  231. m_gameFlags(gameFlags),
  232. m_playerFlags(playerFlags)
  233. {}
  234. virtual bool Evaluate( e_RoundEndReason roundResult, FunFactVector& results ) const
  235. {
  236. if ( !GameQualifies(roundResult, m_gameFlags) )
  237. return false;
  238. int iBestValue = 0;
  239. int iBestPlayer = 0;
  240. bool bResult = false;
  241. bool bTied = false;
  242. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  243. {
  244. CCSPlayer* pPlayer = ToCSPlayer(UTIL_PlayerByIndex( i ) );
  245. if ( pPlayer )
  246. {
  247. if (!PlayerQualifies(pPlayer, m_playerFlags))
  248. continue;
  249. int iValue = m_pfnEval(pPlayer);
  250. if (m_playerFlags & PlayerFlags::HighestOnly)
  251. {
  252. if ( iValue > iBestValue )
  253. {
  254. iBestValue = iValue;
  255. iBestPlayer = i;
  256. bTied = false;
  257. }
  258. else if ( iValue == iBestValue )
  259. {
  260. bTied = true;
  261. }
  262. }
  263. else
  264. {
  265. // generate fun facts for any player who meets the validation requirement
  266. if ( iValue >= m_min )
  267. {
  268. FunFact funfact;
  269. funfact.id = GetId();
  270. funfact.szLocalizationToken = GetLocalizationToken();
  271. funfact.iPlayer = i;
  272. funfact.iData1 = iValue;
  273. funfact.fMagnitude = 1.0f - ((float)m_min / iValue);
  274. results.AddToTail(funfact);
  275. bResult = true;
  276. }
  277. }
  278. }
  279. }
  280. if ( (m_playerFlags & PlayerFlags::HighestOnly) && iBestValue >= m_min && !bTied )
  281. {
  282. FunFact funfact;
  283. funfact.id = GetId();
  284. funfact.szLocalizationToken = GetLocalizationToken();
  285. funfact.iPlayer = iBestPlayer;
  286. funfact.iData1 = iBestValue;
  287. funfact.fMagnitude = 1.0f - ((float)m_min / iBestValue);
  288. results.AddToTail(funfact);
  289. bResult = true;
  290. }
  291. return bResult;
  292. }
  293. private:
  294. PlayerEvalFunction m_pfnEval;
  295. int m_min;
  296. int m_gameFlags;
  297. int m_playerFlags;
  298. };
  299. #define DECLARE_FUNFACT_PLAYERFUNC(funfactId, szLocalizationToken, fCoolness, pfnEval, iMin, gameFlags, playerFlags ) \
  300. static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
  301. { \
  302. return new CFunFact_PlayerEvalFunction(funfactId, szLocalizationToken, fCoolness, pfnEval, iMin, gameFlags, playerFlags); \
  303. }; \
  304. static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
  305. //=============================================================================
  306. // Per-team evaluation Fun Fact
  307. //=============================================================================
  308. typedef bool (*TeamEvalFunction)(int iTeam, int &data1, int &data2, int &data3);
  309. class CFunFact_TeamEvalFunction : public FunFactEvaluator
  310. {
  311. public:
  312. CFunFact_TeamEvalFunction(FunFactId id, const char* szLocalizationToken, float fCoolness, TeamEvalFunction pfnEval, int iTeam, int gameFlags ) :
  313. FunFactEvaluator(id, szLocalizationToken, fCoolness),
  314. m_pfnEval(pfnEval),
  315. m_team(iTeam),
  316. m_gameFlags(gameFlags)
  317. {}
  318. virtual bool Evaluate( e_RoundEndReason roundResult, FunFactVector& results ) const
  319. {
  320. if ( !GameQualifies(roundResult, m_gameFlags) )
  321. return false;
  322. int iData1, iData2, iData3;
  323. if ( m_pfnEval(m_team, iData1, iData2, iData3) )
  324. {
  325. FunFact funfact;
  326. funfact.id = GetId();
  327. funfact.szLocalizationToken = GetLocalizationToken();
  328. funfact.fMagnitude = 0.0f;
  329. results.AddToTail(funfact);
  330. return true;
  331. }
  332. return false;
  333. }
  334. private:
  335. TeamEvalFunction m_pfnEval;
  336. int m_team;
  337. int m_gameFlags;
  338. };
  339. #define DECLARE_FUNFACT_TEAMFUNC(funfactId, szLocalizationToken, fCoolness, pfnEval, iTeam, gameFlags) \
  340. static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
  341. { \
  342. return new CFunFact_TeamEvalFunction(funfactId, szLocalizationToken, fCoolness, pfnEval, iTeam, gameFlags); \
  343. }; \
  344. static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
  345. //=============================================================================
  346. // High Stat-based Fun Fact
  347. // This fun fact will find the player with the highest value for a particular
  348. // stat, and validate when that stat exceeds a specified minimum
  349. //=============================================================================
  350. class CFunFact_StatBest : public FunFactEvaluator
  351. {
  352. public:
  353. CFunFact_StatBest(FunFactId id, const char* szLocalizationToken, float fCoolness, CSStatType_t statId, int iMin, int gameFlags, int playerFlags ) :
  354. FunFactEvaluator(id, szLocalizationToken, fCoolness),
  355. m_statId(statId),
  356. m_min(iMin),
  357. m_gameFlags(gameFlags),
  358. m_playerFlags(playerFlags)
  359. {
  360. V_strncpy(m_singularLocalizationToken, szLocalizationToken, sizeof(m_singularLocalizationToken));
  361. if (m_min == 1)
  362. {
  363. V_strncat(m_singularLocalizationToken, "_singular", sizeof(m_singularLocalizationToken));
  364. }
  365. }
  366. virtual bool Evaluate( e_RoundEndReason roundResult, FunFactVector& results ) const
  367. {
  368. if ( !GameQualifies(roundResult, m_gameFlags) )
  369. return false;
  370. int iBestValue = 0;
  371. int iBestPlayer = 0;
  372. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  373. {
  374. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  375. if ( pPlayer )
  376. {
  377. if ( !PlayerQualifies(pPlayer, m_playerFlags) )
  378. continue;
  379. int iValue = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[m_statId];
  380. if ( iValue > iBestValue )
  381. {
  382. iBestValue = iValue;
  383. iBestPlayer = i;
  384. }
  385. }
  386. }
  387. if ( iBestValue >= m_min )
  388. {
  389. FunFact funfact;
  390. funfact.id = GetId();
  391. funfact.szLocalizationToken = iBestValue == 1 ? m_singularLocalizationToken : GetLocalizationToken();
  392. funfact.iPlayer = iBestPlayer;
  393. funfact.iData1 = iBestValue;
  394. funfact.fMagnitude = 1.0f - ((float)m_min / iBestValue);
  395. results.AddToTail(funfact);
  396. return true;
  397. }
  398. return false;
  399. }
  400. private:
  401. CSStatType_t m_statId;
  402. int m_min;
  403. char m_singularLocalizationToken[128];
  404. int m_gameFlags;
  405. int m_playerFlags;
  406. };
  407. #define DECLARE_FUNFACT_STATBEST(funfactId, szLocalizationToken, fCoolness, statId, iMin, gameFlags, playerFlags) \
  408. static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
  409. { \
  410. return new CFunFact_StatBest(funfactId, szLocalizationToken, fCoolness, statId, iMin, gameFlags, playerFlags); \
  411. }; \
  412. static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
  413. //=============================================================================
  414. // Sum-based Fun Fact
  415. // This fun fact will add up a stat for all players, and is valid when the
  416. // sum exceeds a threshold
  417. //=============================================================================
  418. class CFunFact_StatSum : public FunFactEvaluator
  419. {
  420. public:
  421. CFunFact_StatSum(FunFactId id, const char* szLocalizationToken, float fCoolness, CSStatType_t statId, int iMin, int gameFlags, int playerFlags ) :
  422. FunFactEvaluator(id, szLocalizationToken, fCoolness),
  423. m_statId(statId),
  424. m_min(iMin),
  425. m_gameFlags(gameFlags),
  426. m_playerFlags(playerFlags)
  427. {}
  428. virtual bool Evaluate( e_RoundEndReason roundResult, FunFactVector& results ) const
  429. {
  430. if ( !GameQualifies(roundResult, m_gameFlags) )
  431. return false;
  432. int iSum = 0;
  433. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  434. {
  435. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  436. if ( pPlayer )
  437. {
  438. if (!PlayerQualifies(pPlayer, m_playerFlags))
  439. continue;
  440. iSum += CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[m_statId];
  441. }
  442. }
  443. if ( iSum >= m_min )
  444. {
  445. FunFact funfact;
  446. funfact.id = GetId();
  447. funfact.szLocalizationToken = GetLocalizationToken();
  448. funfact.iPlayer = 0;
  449. funfact.iData1 = iSum;
  450. funfact.fMagnitude = 1.0f - ((float)m_min / iSum);
  451. results.AddToTail(funfact);
  452. return true;
  453. }
  454. return false;
  455. }
  456. private:
  457. CSStatType_t m_statId;
  458. int m_min;
  459. int m_gameFlags;
  460. int m_playerFlags;
  461. };
  462. #define DECLARE_FUNFACT_STATSUM(funfactId, szLocalizationToken, fCoolness, statId, iMin, gameFlags, playerFlags) \
  463. static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
  464. { \
  465. return new CFunFact_StatSum(funfactId, szLocalizationToken, fCoolness, statId, iMin, gameFlags, playerFlags); \
  466. }; \
  467. static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
  468. //=============================================================================
  469. // Helper function to calculate team accuracy
  470. //=============================================================================
  471. float GetTeamAccuracy( int teamNumber )
  472. {
  473. int teamShots = 0;
  474. int teamHits = 0;
  475. //Add up hits and shots
  476. CBasePlayer *pPlayer = NULL;
  477. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  478. {
  479. pPlayer = UTIL_PlayerByIndex( i );
  480. if (pPlayer)
  481. {
  482. if (pPlayer->GetTeamNumber() == teamNumber)
  483. {
  484. teamShots += CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_FIRED];
  485. teamHits += CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_HIT];
  486. }
  487. }
  488. }
  489. if (teamShots > kMinShotsForAccuracy)
  490. return (float)teamHits / teamShots;
  491. return 0.0f;
  492. }
  493. //=============================================================================
  494. // fun fact explicit evaluation functions
  495. //=============================================================================
  496. bool FFEVAL_ALWAYS_TRUE( int &iPlayer, int &data1, int &data2, int &data3 )
  497. {
  498. return true;
  499. }
  500. bool FFEVAL_CT_WIN_NO_KILLS( int &iPlayer, int &data1, int &data2, int &data3 )
  501. {
  502. return ( CSGameRules()->m_iRoundWinStatus == WINNER_CT && CSGameRules()->m_bNoTerroristsKilled );
  503. }
  504. bool FFEVAL_T_WIN_NO_KILLS( int &iPlayer, int &data1, int &data2, int &data3 )
  505. {
  506. return ( CSGameRules()->m_iRoundWinStatus == WINNER_TER && CSGameRules()->m_bNoCTsKilled );
  507. }
  508. bool FFEVAL_T_WIN_NO_CASUALTIES( int &iPlayer, int &data1, int &data2, int &data3 )
  509. {
  510. return ( CSGameRules()->m_iRoundWinStatus == WINNER_TER && CSGameRules()->m_bNoTerroristsKilled );
  511. }
  512. bool FFEVAL_CT_WIN_NO_CASUALTIES( int &iPlayer, int &data1, int &data2, int &data3 )
  513. {
  514. return ( CSGameRules()->m_iRoundWinStatus == WINNER_CT && CSGameRules()->m_bNoCTsKilled );
  515. }
  516. int FFEVAL_KILLED_DEFUSER( CCSPlayer* pPlayer )
  517. {
  518. return pPlayer->GetKilledDefuser() ? 1 : 0;
  519. }
  520. int FFEVAL_KILLED_RESCUER( CCSPlayer* pPlayer )
  521. {
  522. return pPlayer->GetKilledRescuer() ? 1 : 0;
  523. }
  524. int FFEVAL_KILLS_SINGLE_GRENADE( CCSPlayer* pPlayer )
  525. {
  526. return pPlayer->GetMaxGrenadeKills();
  527. }
  528. int FFEVAL_DAMAGE_NO_KILLS( CCSPlayer* pPlayer )
  529. {
  530. if (CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_KILLS] == 0)
  531. return CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_DAMAGE];
  532. else
  533. return 0;
  534. }
  535. bool FFEVAL_FIRST_KILL( int &iPlayer, int &data1, int &data2, int &data3 )
  536. {
  537. const int timeRequired = CSGameRules()->IsPlayingClassic() ? kFirstKillTimeClassic : kFirstKillTimeDemolition;
  538. if ( CSGameRules()->m_pFirstKill != NULL && CSGameRules()->m_firstKillTime <= timeRequired )
  539. {
  540. iPlayer = CSGameRules()->m_pFirstKill->entindex();
  541. data1 = CSGameRules()->m_firstKillTime;
  542. return true;
  543. }
  544. return false;
  545. }
  546. bool FFEVAL_FIRST_BLOOD( int &iPlayer, int &data1, int &data2, int &data3 )
  547. {
  548. const int timeRequired = CSGameRules()->IsPlayingClassic() ? kFirstBloodTimeClassic : kFirstBloodTimeDemolition;
  549. if ( CSGameRules()->m_pFirstBlood != NULL && CSGameRules()->m_firstBloodTime < timeRequired )
  550. {
  551. iPlayer = CSGameRules()->m_pFirstBlood->entindex();
  552. data1 = CSGameRules()->m_firstKillTime;
  553. return true;
  554. }
  555. return false;
  556. }
  557. bool FFEVAL_CT_WIN_TIME( int &iPlayer, int &data1, int &data2, int &data3 )
  558. {
  559. if ( CSGameRules()->GetRoundElapsedTime() < kFastRoundTimeElimination && CSGameRules()->m_iRoundWinStatus == WINNER_CT )
  560. {
  561. data1 = CSGameRules()->GetRoundElapsedTime();
  562. return true;
  563. }
  564. return false;
  565. }
  566. bool FFEVAL_TER_WIN_TIME( int &iPlayer, int &data1, int &data2, int &data3 )
  567. {
  568. if ( CSGameRules()->GetRoundElapsedTime() < kFastRoundTimeElimination && CSGameRules()->m_iRoundWinStatus == WINNER_TER )
  569. {
  570. data1 = CSGameRules()->GetRoundElapsedTime();
  571. return true;
  572. }
  573. return false;
  574. }
  575. bool FFEVAL_SHORT_ROUND( int &iPlayer, int &data1, int &data2, int &data3 )
  576. {
  577. if ( CSGameRules()->GetRoundElapsedTime() < kFastRoundTimeClassic )
  578. {
  579. data1 = CSGameRules()->GetRoundElapsedTime();
  580. return true;
  581. }
  582. return false;
  583. }
  584. int FFEVAL_ACCURACY( CCSPlayer* pPlayer )
  585. {
  586. float shots = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_FIRED];
  587. float hits = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_HIT];
  588. if (shots >= kMinShotsForAccuracy)
  589. return RoundFloatToInt(100.0f * hits / shots);
  590. return 0;
  591. }
  592. int FFEVAL_EMPTY_GUNS( CCSPlayer* pPlayer )
  593. {
  594. return pPlayer->GetNumFirearmsRanOutOfAmmo();
  595. }
  596. int FFEVAL_QUARTER_HEALTH( CCSPlayer* pPlayer )
  597. {
  598. return pPlayer->GetMediumHealthKills();
  599. }
  600. int FFEVAL_STEPS_TAKEN( CCSPlayer* pPlayer )
  601. {
  602. return pPlayer->GetNumFootsteps();
  603. }
  604. int FFEVAL_MOST_CONCURRENT_DOMINATIONS( CCSPlayer* pPlayer )
  605. {
  606. return pPlayer->GetNumConcurrentDominations();
  607. }
  608. int FFEVAL_MOST_BOTS_ASSUMED( CCSPlayer* pPlayer )
  609. {
  610. return pPlayer->GetNumBotsControlled();
  611. }
  612. int FFEVAL_HIGHEST_PROXIMITY_SCORE( CCSPlayer* pPlayer )
  613. {
  614. return pPlayer->GetRoundProximityScore();
  615. }
  616. int FFEVAL_ROUNDS_WITHOUT_DYING( CCSPlayer* pPlayer )
  617. {
  618. return pPlayer->GetCurNumRoundsSurvived();
  619. }
  620. int FFEVAL_DEFAULT_WEAPON( CCSPlayer* pPlayer )
  621. {
  622. if ( CSGameRules()->IsPistolRound() )
  623. return 0;
  624. return pPlayer->GetPickedUpWeaponThisRound() ? 0 : 1;
  625. }
  626. int FFEVAL_KILLED_PERCENT_OF_ENEMIES( CCSPlayer* pPlayer )
  627. {
  628. return pPlayer->GetPercentageOfEnemyTeamKilled();
  629. }
  630. int FFEVAL_MAX_NUM_RESPAWNS( CCSPlayer* pPlayer )
  631. {
  632. return pPlayer->m_iNumSpawns;
  633. }
  634. int FFEVAL_KILL_STREAK_BEFORE_DYING( CCSPlayer* pPlayer )
  635. {
  636. return pPlayer->m_maxNumEnemiesKillStreak;
  637. }
  638. void GetTeamRoundScore( int teamNumber, int* num_players,int* contribution_sum )
  639. {
  640. CBasePlayer *basePlayer = NULL;
  641. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  642. {
  643. basePlayer = UTIL_PlayerByIndex( i );
  644. CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
  645. if( pPlayer && pPlayer->GetTeamNumber() == teamNumber )
  646. {
  647. (*num_players)++;
  648. (*contribution_sum) += pPlayer->GetRoundContributionScore();
  649. }
  650. }
  651. }
  652. bool FFEVAL_FAILED_BOMB_PLANTS( int &iPlayer, int &data1, int &data2, int &data3 )
  653. {
  654. data1 = 0;
  655. CBasePlayer *basePlayer = NULL;
  656. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  657. {
  658. basePlayer = UTIL_PlayerByIndex( i );
  659. CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
  660. if ( pPlayer &&
  661. !(pPlayer->GetBombPlacedTime() >= 0.0f) &&
  662. pPlayer->HasAttemptedBombPlace() )
  663. {
  664. iPlayer = i;
  665. data1++;
  666. }
  667. }
  668. if ( data1 >= 2 )
  669. {
  670. return true;
  671. }
  672. return false;
  673. }
  674. bool FFEVAL_BOMB_PLANTED_BEFORE_KILL( int &iPlayer, int &data1, int &data2, int &data3 )
  675. {
  676. float fBombPlantedTime = -1.0f;
  677. float fFirstKillTime = -1.0f;
  678. CBasePlayer *basePlayer = NULL;
  679. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  680. {
  681. basePlayer = UTIL_PlayerByIndex( i );
  682. CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
  683. if ( pPlayer &&
  684. pPlayer->GetBombPlacedTime() >= 0.0f &&
  685. ( pPlayer->GetBombPlacedTime() <= fBombPlantedTime || fBombPlantedTime < 0.0f ) )
  686. {
  687. iPlayer = i;
  688. fBombPlantedTime = pPlayer->GetBombPlacedTime();
  689. data1 = (int) fBombPlantedTime;
  690. }
  691. if (pPlayer &&
  692. pPlayer->GetKilledTime() > 0.0f &&
  693. (fFirstKillTime < 0.0f || pPlayer->GetKilledTime() < fFirstKillTime ) )
  694. {
  695. fFirstKillTime = pPlayer->GetKilledTime();
  696. data2 = (int) fFirstKillTime;
  697. }
  698. }
  699. if( fBombPlantedTime > 0.0f &&
  700. (fFirstKillTime <= 0.0f || fFirstKillTime > fBombPlantedTime ) )
  701. {
  702. return true;
  703. }
  704. return false;
  705. }
  706. bool FFEVAL_PICKUP_BOMB( int &iPlayer, int &data1, int &data2, int &data3 )
  707. {
  708. // count number of picker-upers as data1
  709. data1 = 0;
  710. CBasePlayer *basePlayer = NULL;
  711. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  712. {
  713. basePlayer = UTIL_PlayerByIndex( i );
  714. CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
  715. if( pPlayer && pPlayer->GetBombPickuptime() >= 0.0f )
  716. data1++;
  717. }
  718. if( data1 > 2 ) // enough guys picked up the bomb to be interesting
  719. {
  720. // find someone who placed a bomb
  721. CBasePlayer *basePlayer = NULL;
  722. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  723. {
  724. basePlayer = UTIL_PlayerByIndex( i );
  725. CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
  726. if( pPlayer && pPlayer->GetBombPlacedTime() >= 0.0f )
  727. {
  728. data1--;
  729. iPlayer = i;
  730. return true;
  731. }
  732. }
  733. }
  734. return false;
  735. }
  736. bool FFEVAL_TICKING_TIME( int &iPlayer, int &data1, int &data2, int &data3 )
  737. {
  738. int winningTeam = CSGameRules()->m_iRoundWinStatus;
  739. if ( winningTeam != TEAM_TERRORIST )
  740. return false;
  741. data1 = 0;
  742. CBasePlayer *basePlayer = NULL;
  743. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  744. {
  745. basePlayer = UTIL_PlayerByIndex( i );
  746. CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
  747. if( pPlayer && pPlayer->AttemptedToDefuseBomb() )
  748. data1++;
  749. }
  750. if( data1 > 1 )
  751. return true; // we need at least two of these guys to be interesting
  752. return false;
  753. }
  754. bool FFEVAL_KNIFE_LEVEL_REACHED( int &iPlayer, int &data1, int &data2, int &data3 )
  755. {
  756. CBasePlayer *basePlayer = NULL;
  757. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  758. {
  759. basePlayer = UTIL_PlayerByIndex( i );
  760. CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
  761. CWeaponCSBase* attackerWeapon = pPlayer ? pPlayer->GetActiveCSWeapon() : 0;
  762. CSWeaponID attackerWeaponID = attackerWeapon ? attackerWeapon->GetCSWeaponID() : WEAPON_NONE;
  763. if( attackerWeaponID == WEAPON_KNIFE )
  764. data1++;
  765. }
  766. if( data1 > 3 )
  767. return true; // we need at least three of these guys to be interesting
  768. return false;
  769. }
  770. bool FFEVAL_WON_AS_LAST_MEMBER( int &iPlayer, int &data1, int &data2, int &data3 )
  771. {
  772. CCSPlayer *pCSPlayer = NULL;
  773. int winningTeam = CSGameRules()->m_iRoundWinStatus;
  774. if (winningTeam != TEAM_TERRORIST && winningTeam != TEAM_CT)
  775. {
  776. return false;
  777. }
  778. int losingTeam = (winningTeam == TEAM_TERRORIST) ? TEAM_CT : TEAM_TERRORIST;
  779. CCSGameRules::TeamPlayerCounts playerCounts[TEAM_MAXCOUNT];
  780. CSGameRules()->GetPlayerCounts(playerCounts);
  781. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  782. {
  783. pCSPlayer = ToCSPlayer(UTIL_PlayerByIndex( i ) );
  784. if( pCSPlayer && pCSPlayer->GetTeamNumber() == winningTeam && pCSPlayer->IsAlive())
  785. {
  786. //Check if the player is still the only living member of his team ( on the off chance that a player joins late)
  787. //This check is a little hacky. We make sure that there are no enemies alive. Since the bomb causes the round to end before exploding,
  788. //the only way for only 1 person to be alive at round win time is extermination or defuse (in both cases, the last living player caused the win)
  789. if (playerCounts[winningTeam].totalAlivePlayers == 1 && playerCounts[losingTeam].totalAlivePlayers == 0)
  790. {
  791. const PlayerStats_t& playerStats = CCS_GameStats.FindPlayerStats( pCSPlayer );
  792. iPlayer = i;
  793. data1 = playerStats.statsCurrentRound[CSSTAT_KILLS_WHILE_LAST_PLAYER_ALIVE];
  794. if (data1 >= 2)
  795. {
  796. return true;
  797. }
  798. }
  799. }
  800. }
  801. return false;
  802. }
  803. int FFEVAL_PULLED_TRIGGER( CCSPlayer* pPlayer )
  804. {
  805. return pPlayer->GetNumTriggerPulls();
  806. }
  807. int FFEVAL_SURVIVAL_TIME( CCSPlayer* pPlayer )
  808. {
  809. return (int) pPlayer->GetLongestSurvivalTime();
  810. }
  811. int FFEVAL_MOLOTOV_BURNS( CCSPlayer* pPlayer )
  812. {
  813. // DDK: note that this gets all burn damage. Currently only molotov's have burn damage
  814. return pPlayer->GetNumPlayersDamagedWithFire();
  815. }
  816. int FFEVAL_KNIFE_WITHOUT_AMMO( CCSPlayer* pPlayer )
  817. {
  818. return pPlayer->GetKnifeKillsWhenOutOfAmmo();
  819. }
  820. int FFEVAL_KNIFE_BOMB_PLANTER( CCSPlayer* pPlayer )
  821. {
  822. return pPlayer->GetKnifeLevelKilledBombPlacer();
  823. }
  824. int FFEVAL_BOMB_CARRIERS( CCSPlayer* pPlayer )
  825. {
  826. return pPlayer->GetNumBombCarrierKills();
  827. }
  828. int FFEVAL_KNIFE_IN_GUNFIGHT( CCSPlayer* pPlayer )
  829. {
  830. return pPlayer->WasWieldingKnifeAndKilledByGun() ? 1 : 0;
  831. }
  832. int FFEVAL_MULTIPLE_ATTACKER_COUNT( CCSPlayer* pPlayer )
  833. {
  834. return pPlayer->GetNumEnemyDamagers();
  835. }
  836. int FFEVAL_USED_ALL_AMMO( CCSPlayer* pPlayer )
  837. {
  838. CWeaponCSBase *pRifleWeapon = dynamic_cast< CWeaponCSBase * >(pPlayer->Weapon_GetSlot( WEAPON_SLOT_RIFLE ));
  839. CWeaponCSBase *pHandgunWeapon = dynamic_cast< CWeaponCSBase * >(pPlayer->Weapon_GetSlot( WEAPON_SLOT_PISTOL ));
  840. if ( pRifleWeapon && !pRifleWeapon->HasAmmo() && pHandgunWeapon && !pHandgunWeapon->HasAmmo() )
  841. return 1;
  842. else
  843. return 0;
  844. }
  845. int FFEVAL_DAMAGE_MULTIPLE_ENEMIES( CCSPlayer* pPlayer )
  846. {
  847. return pPlayer->GetNumEnemiesDamaged();
  848. }
  849. int FFEVAL_USED_MULTIPLE_WEAPONS( CCSPlayer* pPlayer )
  850. {
  851. return pPlayer->GetNumFirearmsUsed();
  852. }
  853. int FFEVAL_DEFUSED_WITH_DROPPED_KIT( CCSPlayer* pPlayer )
  854. {
  855. return pPlayer->GetDefusedWithPickedUpKit() ? 1 : 0;
  856. }
  857. bool funfact_helper_defuse_close_call( int &iPlayer, int &data1, int &data2, int &data3, float flThreshold, float flFractionalMultiplier )
  858. {
  859. CBasePlayer *pPlayer = NULL;
  860. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  861. {
  862. pPlayer = UTIL_PlayerByIndex( i );
  863. // Only CTs can defuse
  864. if ( pPlayer && pPlayer->GetTeamNumber() == TEAM_CT )
  865. {
  866. CCSPlayer *pCSPlayer = (CCSPlayer*)pPlayer;
  867. if ( pCSPlayer )
  868. {
  869. float flDefuseTimeRemaining = pCSPlayer->GetDefusedBombWithThisTimeRemaining();
  870. if ( flDefuseTimeRemaining > 0 && flDefuseTimeRemaining <= flThreshold )
  871. {
  872. iPlayer = i;
  873. data1 = RoundFloatToInt( flDefuseTimeRemaining * flFractionalMultiplier );
  874. return true;
  875. }
  876. }
  877. }
  878. }
  879. return false;
  880. }
  881. bool FFEVAL_DEFUSE_WAS_CLOSE_CALL_TENTHS( int &iPlayer, int &data1, int &data2, int &data3 )
  882. {
  883. return funfact_helper_defuse_close_call( iPlayer, data1, data2, data3, 0.5f, 10.0f );
  884. }
  885. bool FFEVAL_DEFUSE_WAS_CLOSE_CALL_HUNDREDTHS( int &iPlayer, int &data1, int &data2, int &data3 )
  886. {
  887. return funfact_helper_defuse_close_call( iPlayer, data1, data2, data3, 0.1f, 100.0f );
  888. }
  889. bool FFEVAL_DEFUSE_WAS_CLOSE_CALL_THOUSANDTHS( int &iPlayer, int &data1, int &data2, int &data3 )
  890. {
  891. return funfact_helper_defuse_close_call( iPlayer, data1, data2, data3, 0.01f, 1000.0f );
  892. }
  893. bool FFEVAL_T_ACCURACY( int &iPlayer, int &data1, int &data2, int &data3 )
  894. {
  895. float terroristAccuracy = GetTeamAccuracy(TEAM_TERRORIST);
  896. float ctAccuracy = GetTeamAccuracy(TEAM_CT);
  897. if (terroristAccuracy > kMinTeamAccuracy && terroristAccuracy > ctAccuracy)
  898. {
  899. data1 = RoundFloatToInt(terroristAccuracy * 100.0f);
  900. return true;
  901. }
  902. return false;
  903. }
  904. bool FFEVAL_CT_ACCURACY( int &iPlayer, int &data1, int &data2, int &data3 )
  905. {
  906. float terroristAccuracy = GetTeamAccuracy(TEAM_TERRORIST);
  907. float ctAccuracy = GetTeamAccuracy(TEAM_CT);
  908. if (ctAccuracy > kMinTeamAccuracy && ctAccuracy > terroristAccuracy)
  909. {
  910. data1 = RoundFloatToInt(ctAccuracy * 100.0f);
  911. return true;
  912. }
  913. return false;
  914. }
  915. bool FFEVAL_BEST_TERRORIST_ACCURACY( int &iPlayer, int &data1, int &data2, int &data3 )
  916. {
  917. float fAccuracy = 0.0f, fBestAccuracy = 0.0f;
  918. CBasePlayer *pPlayer = NULL;
  919. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  920. {
  921. pPlayer = UTIL_PlayerByIndex( i );
  922. // Look only at terrorist players
  923. if ( pPlayer && pPlayer->GetTeamNumber() == TEAM_TERRORIST )
  924. {
  925. // Calculate accuracy the terrorist
  926. float shots = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_FIRED];
  927. float hits = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_HIT];
  928. if (shots > kMinShotsForAccuracy)
  929. {
  930. fAccuracy = (float)hits / shots;
  931. }
  932. // Track the most accurate terrorist
  933. if ( fAccuracy > fBestAccuracy )
  934. {
  935. fBestAccuracy = fAccuracy;
  936. iPlayer = i;
  937. }
  938. }
  939. }
  940. if ( fBestAccuracy - GetTeamAccuracy( TEAM_TERRORIST ) >= kAccuracyAdvantage )
  941. {
  942. data1 = RoundFloatToInt(fBestAccuracy * 100.0f);
  943. data2 = RoundFloatToInt(GetTeamAccuracy( TEAM_TERRORIST ) * 100.0f);
  944. return true;
  945. }
  946. return false;
  947. }
  948. bool FFEVAL_BEST_COUNTERTERRORIST_ACCURACY( int &iPlayer, int &data1, int &data2, int &data3 )
  949. {
  950. float fAccuracy = 0.0f, fBestAccuracy = 0.0f;
  951. CBasePlayer *pPlayer = NULL;
  952. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  953. {
  954. pPlayer = UTIL_PlayerByIndex( i );
  955. // Look only at counter-terrorist players
  956. if ( pPlayer && pPlayer->GetTeamNumber() == TEAM_CT )
  957. {
  958. // Calculate accuracy the counter-terrorist
  959. float shots = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_FIRED];
  960. float hits = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_HIT];
  961. if (shots > kMinShotsForAccuracy)
  962. {
  963. fAccuracy = (float)hits / shots;
  964. }
  965. // Track the most accurate counter-terrorist
  966. if ( fAccuracy > fBestAccuracy )
  967. {
  968. fBestAccuracy = fAccuracy;
  969. iPlayer = i;
  970. }
  971. }
  972. }
  973. if ( fBestAccuracy - GetTeamAccuracy( TEAM_CT ) >= kAccuracyAdvantage )
  974. {
  975. data1 = RoundFloatToInt(fBestAccuracy * 100.0f);
  976. data2 = RoundFloatToInt(GetTeamAccuracy( TEAM_CT ) * 100.0f);
  977. return true;
  978. }
  979. return false;
  980. }
  981. //=============================================================================
  982. // Fun Fact Declarations
  983. //=============================================================================
  984. DECLARE_FUNFACT_STATBEST( FF_KILL_WOUNDED_ENEMIES, "#funfact_kill_wounded_enemies", 0.2f, CSSTAT_KILLS_ENEMY_WOUNDED, 3, GameFlags::AllModes | GameFlags::Elimination, PlayerFlags::HighestOnly );
  985. DECLARE_FUNFACT_STATBEST( FF_KILLS_HEADSHOTS, "#funfact_kills_headshots", 0.4f, CSSTAT_KILLS_HEADSHOT, 2, GameFlags::AllModes, PlayerFlags::HighestOnly );
  986. DECLARE_FUNFACT_STATBEST( FF_KILLS_WITH_STATTRAK_WEAPON, "#funfact_kills_with_stattrak_weapon", 0.3f, CSSTAT_KILLS_WITH_STATTRAK_WEAPON, 3, GameFlags::AllModes, PlayerFlags::HighestOnly );
  987. DECLARE_FUNFACT_STATBEST( FF_NUM_TIMES_JUMPED, "#funfact_num_times_jumped", 0.1f, CSSTAT_TOTAL_JUMPS, 20, GameFlags::AllModes, PlayerFlags::HighestOnly );
  988. DECLARE_FUNFACT_STATBEST( FF_KILLED_ENEMIES, "#funfact_killed_enemies", 0.4f, CSSTAT_KILLS, 3, GameFlags::NotGunGame | GameFlags::Elimination, PlayerFlags::HighestOnly );
  989. DECLARE_FUNFACT_STATBEST( FF_KNIFE_KILLS, "#funfact_knife_kills", 0.4f, CSSTAT_KILLS_KNIFE, 1, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
  990. DECLARE_FUNFACT_STATBEST( FF_REVENGE, "#funfact_revenge", 0.3f, CSSTAT_REVENGES, 1, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
  991. DECLARE_FUNFACT_STATBEST( FF_BLIND_KILLS, "#funfact_blind_kills", 0.9f, CSSTAT_KILLS_WHILE_BLINDED, 1, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
  992. DECLARE_FUNFACT_STATBEST( FF_DAMAGE_WITH_GRENADES, "#funfact_damage_with_grenade", 0.5f, CSSTAT_GRENADE_DAMAGE, 150, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
  993. DECLARE_FUNFACT_STATBEST( FF_KILLS_WITH_GRENADES, "#funfact_kills_grenades", 0.7f, CSSTAT_KILLS_HEGRENADE, 2, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
  994. DECLARE_FUNFACT_STATBEST( FF_POSTHUMOUS_GRENADE_KILLS,"#funfact_posthumous_kills_with_grenade", 1.0f, CSSTAT_GRENADE_POSTHUMOUSKILLS, 1, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
  995. DECLARE_FUNFACT_STATBEST( FF_GRENADES_THROWN, "#funfact_grenades_thrown", 0.3f, CSSTAT_GRENADES_THROWN, 3, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
  996. DECLARE_FUNFACT_STATBEST( FF_DEFENDED_BOMB, "#funfact_defended_bomb", 0.5f, CSSTAT_KILLS_WHILE_DEFENDING_BOMB, 2, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
  997. DECLARE_FUNFACT_STATBEST( FF_KILLS_WITH_LAST_ROUND, "#funfact_kills_with_last_round", 0.6f, CSSTAT_KILLS_WITH_LAST_ROUND, 1, GameFlags::Classic, PlayerFlags::HighestOnly );
  998. DECLARE_FUNFACT_STATBEST( FF_DONATED_WEAPONS, "#funfact_donated_weapons", 0.3f, CSSTAT_WEAPONS_DONATED, 2, GameFlags::Classic, PlayerFlags::HighestOnly );
  999. DECLARE_FUNFACT_STATBEST( FF_ITEMS_PURCHASED, "#funfact_items_purchased", 0.2f, CSSTAT_ITEMS_PURCHASED, 4, GameFlags::Classic, PlayerFlags::HighestOnly );
  1000. DECLARE_FUNFACT_STATBEST( FF_FALL_DAMAGE, "#funfact_fall_damage", 0.2f, CSSTAT_FALL_DAMAGE, 50, GameFlags::Classic, PlayerFlags::HighestOnly );
  1001. DECLARE_FUNFACT_STATBEST( FF_BROKE_WINDOWS, "#funfact_broke_windows", 0.1f, CSSTAT_NUM_BROKEN_WINDOWS, 5, GameFlags::Classic, PlayerFlags::HighestOnly );
  1002. DECLARE_FUNFACT_STATBEST( FF_TASER_KILL, "#funfact_taser_kill", 0.7f, CSSTAT_KILLS_TASER, 1, GameFlags::Classic, PlayerFlags::HighestOnly );
  1003. DECLARE_FUNFACT_STATBEST( FF_NUMBER_OF_OVERKILLS, "#funfact_number_of_overkills", 0.5f, CSSTAT_DOMINATION_OVERKILLS, 2, GameFlags::Classic, PlayerFlags::HighestOnly );
  1004. DECLARE_FUNFACT_STATBEST( FF_MONEY_SPENT, "#funfact_money_spent", 0.2f, CSSTAT_MONEY_SPENT, 5000, GameFlags::Classic, PlayerFlags::HighestOnly );
  1005. DECLARE_FUNFACT_STATBEST( FF_ITEMS_DROPPED_VALUE, "#funfact_items_dropped_value", 0.4f, CSTAT_ITEMS_DROPPED_VALUE, 10000, GameFlags::Classic, PlayerFlags::HighestOnly );
  1006. DECLARE_FUNFACT_STATSUM( FF_SHOTS_FIRED, "#funfact_shots_fired", 0.2f, CSSTAT_SHOTS_FIRED, 100, GameFlags::NotGunGame, PlayerFlags::All );
  1007. DECLARE_FUNFACT_STATSUM( FF_SHOTS_FIRED_GG, "#funfact_shots_fired", 0.1f, CSSTAT_SHOTS_FIRED, 250, GameFlags::GunGame, PlayerFlags::All );
  1008. DECLARE_FUNFACT_PLAYERFUNC( FF_BEST_ACCURACY, "#funfact_best_accuracy", 0.2f, FFEVAL_ACCURACY, 50, GameFlags::AllModes, PlayerFlags::HighestOnly | PlayerFlags::Alive );
  1009. DECLARE_FUNFACT_PLAYERFUNC( FF_BOTS_ASSUMED, "#funfact_bots_assumed", 0.2f, FFEVAL_MOST_BOTS_ASSUMED, 2, GameFlags::AllModes, PlayerFlags::HighestOnly | PlayerFlags::AllowIfControlledBot );
  1010. DECLARE_FUNFACT_PLAYERFUNC( FF_DOMINATION, "#funfact_domination", 0.3f, FFEVAL_MOST_CONCURRENT_DOMINATIONS, 1, GameFlags::AllModes, PlayerFlags::HighestOnly | PlayerFlags::HasKilledThisRound );
  1011. DECLARE_FUNFACT_PLAYERFUNC( FF_USED_ALL_AMMO, "#funfact_used_all_ammo", 0.5f, FFEVAL_USED_ALL_AMMO, 1, GameFlags::NotGunGame, PlayerFlags::All );
  1012. DECLARE_FUNFACT_PLAYERFUNC( FF_DAMAGE_NO_KILLS, "#funfact_damage_no_kills", 0.2f, FFEVAL_DAMAGE_NO_KILLS, 250, GameFlags::NotGunGame, PlayerFlags::All );
  1013. DECLARE_FUNFACT_PLAYERFUNC( FF_SINGLE_GRENADE_KILLS, "#funfact_kills_with_single_grenade", 0.8f, FFEVAL_KILLS_SINGLE_GRENADE, 2, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
  1014. DECLARE_FUNFACT_PLAYERFUNC( FF_EMPTY_GUNS, "#funfact_empty_guns", 0.3f, FFEVAL_EMPTY_GUNS, 2, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
  1015. DECLARE_FUNFACT_PLAYERFUNC( FF_MULTIPLE_ATTACKS_LIVED, "#funfact_survived_multiple_attackers", 0.4f, FFEVAL_MULTIPLE_ATTACKER_COUNT, 3, GameFlags::NotGunGame, PlayerFlags::HighestOnly | PlayerFlags::Alive );
  1016. DECLARE_FUNFACT_PLAYERFUNC( FF_MULTIPLE_ATTACKS_DIED, "#funfact_died_from_multiple_attackers", 0.3f, FFEVAL_MULTIPLE_ATTACKER_COUNT, 3, GameFlags::NotGunGame, PlayerFlags::HighestOnly | PlayerFlags::Dead );
  1017. DECLARE_FUNFACT_PLAYERFUNC( FF_DAMAGE_MULTIPLE_ENEMIES, "#funfact_damage_multiple_enemies", 0.2f, FFEVAL_DAMAGE_MULTIPLE_ENEMIES, 3, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
  1018. DECLARE_FUNFACT_PLAYERFUNC( FF_BOMB_CARRIERS, "#funfact_bomb_carriers", 0.3f, FFEVAL_BOMB_CARRIERS, 2, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
  1019. DECLARE_FUNFACT_PLAYERFUNC( FF_KNIFE_BOMB_PLANTER, "#funfact_knife_bomb_planter", 0.8f, FFEVAL_KNIFE_BOMB_PLANTER, 1, GameFlags::NotGunGame, PlayerFlags::HighestOnly | PlayerFlags::Alive );
  1020. DECLARE_FUNFACT_PLAYERFUNC( FF_KNIFE_WITHOUT_AMMO, "#funfact_knife_without_ammo", 0.3f, FFEVAL_KNIFE_WITHOUT_AMMO, 2, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
  1021. DECLARE_FUNFACT_PLAYERFUNC( FF_KILL_DEFUSER, "#funfact_kill_defuser", 0.5f, FFEVAL_KILLED_DEFUSER, 1, GameFlags::NotGunGame, PlayerFlags::TeamTerrorist | PlayerFlags::WinningTeam );
  1022. DECLARE_FUNFACT_PLAYERFUNC( FF_KILL_RESCUER, "#funfact_kill_rescuer", 0.5f, FFEVAL_KILLED_RESCUER, 1, GameFlags::Classic, PlayerFlags::TeamTerrorist );
  1023. DECLARE_FUNFACT_PLAYERFUNC( FF_DEFAULT_WEAPON, "#funfact_default_weapon", 0.1f, FFEVAL_DEFAULT_WEAPON, 1, GameFlags::Classic, PlayerFlags::WinningTeam | PlayerFlags::HasKilledThisRound | PlayerFlags::Alive );
  1024. DECLARE_FUNFACT_PLAYERFUNC( FF_ROUNDS_WITHOUT_DYING, "#funfact_rounds_without_dying", 0.6f, FFEVAL_ROUNDS_WITHOUT_DYING, 3, GameFlags::Classic, PlayerFlags::HighestOnly | PlayerFlags::WinningTeam | PlayerFlags::Alive );
  1025. DECLARE_FUNFACT_PLAYERFUNC( FF_KNIFE_IN_GUNFIGHT, "#funfact_knife_in_gunfight", 0.4f, FFEVAL_KNIFE_IN_GUNFIGHT , 1, GameFlags::Classic, PlayerFlags::All );
  1026. DECLARE_FUNFACT_PLAYERFUNC( FF_USED_MULTIPLE_WEAPONS, "#funfact_used_multiple_weapons", 0.5f, FFEVAL_USED_MULTIPLE_WEAPONS, 4, GameFlags::Classic, PlayerFlags::HighestOnly );
  1027. DECLARE_FUNFACT_PLAYERFUNC( FF_DEFUSED_WITH_DROPPED_KIT,"#funfact_defused_with_dropped_kit", 0.5f, FFEVAL_DEFUSED_WITH_DROPPED_KIT, 1, GameFlags::Classic, PlayerFlags::TeamCT );
  1028. DECLARE_FUNFACT_PLAYERFUNC( FF_KILLED_HALF_OF_ENEMIES, "#funfact_killed_half_of_enemies", 0.4f, FFEVAL_KILLED_PERCENT_OF_ENEMIES, 50, GameFlags::NotGunGame, PlayerFlags::WinningTeam | PlayerFlags::HighestOnly );
  1029. DECLARE_FUNFACT_PLAYERFUNC( FF_KILLED_ALL_ENEMIES, "#funfact_ace", 0.9f, FFEVAL_KILLED_PERCENT_OF_ENEMIES, 100, GameFlags::Classic, PlayerFlags::WinningTeam | PlayerFlags::HighestOnly );
  1030. DECLARE_FUNFACT_PLAYERFUNC( FF_MOLOTOV_BURNS, "#funfact_molotov_burns", 0.3f, FFEVAL_MOLOTOV_BURNS, 2, GameFlags::Classic, PlayerFlags::HighestOnly );
  1031. DECLARE_FUNFACT_PLAYERFUNC( FF_MAX_KILLED_BEFORE_DYING, "#funfact_killed_before_dying", 0.3f, FFEVAL_KILL_STREAK_BEFORE_DYING, 3, GameFlags::GunGame, PlayerFlags::HighestOnly );
  1032. DECLARE_FUNFACT_PLAYERFUNC( FF_MAX_RESPAWNS, "#funfact_respawned", 0.3f, FFEVAL_MAX_NUM_RESPAWNS, 3, GameFlags::GunGame, PlayerFlags::HighestOnly );
  1033. DECLARE_FUNFACT_PLAYERFUNC( FF_SURVIVAL_TIME, "#funfact_survival_time", 0.2f, FFEVAL_SURVIVAL_TIME, 20, GameFlags::GunGame, PlayerFlags::HighestOnly );
  1034. DECLARE_FUNFACT_PLAYERFUNC( FF_PULLED_TRIGGER, "#funfact_pulled_trigger", 0.1f, FFEVAL_PULLED_TRIGGER, 20, GameFlags::GunGame, PlayerFlags::HighestOnly );
  1035. DECLARE_FUNFACT_PLAYERFUNC( FF_STEPS_TAKEN, "#funfact_steps_taken", 0.1f, FFEVAL_STEPS_TAKEN, 100, GameFlags::GunGame, PlayerFlags::HighestOnly );
  1036. DECLARE_FUNFACT_PLAYERFUNC( FF_QUARTER_HEALTH, "#funfact_quarter_health", 0.3f, FFEVAL_QUARTER_HEALTH, 2, GameFlags::GunGame, PlayerFlags::HighestOnly );
  1037. DECLARE_FUNFACT_EVALFUNC( FF_DEFUSE_WAS_CLOSE_CALL_TENTHS, "#funfact_defuse_was_close_call_tenths", 0.8f, FFEVAL_DEFUSE_WAS_CLOSE_CALL_TENTHS, GameFlags::Classic );
  1038. DECLARE_FUNFACT_EVALFUNC( FF_DEFUSE_WAS_CLOSE_CALL_HUNDREDTHS, "#funfact_defuse_was_close_call_hundredths", 0.9f, FFEVAL_DEFUSE_WAS_CLOSE_CALL_HUNDREDTHS, GameFlags::Classic );
  1039. DECLARE_FUNFACT_EVALFUNC( FF_DEFUSE_WAS_CLOSE_CALL_THOUSANDTHS, "#funfact_defuse_was_close_call_thousandths", 1.0f, FFEVAL_DEFUSE_WAS_CLOSE_CALL_THOUSANDTHS, GameFlags::Classic );
  1040. DECLARE_FUNFACT_EVALFUNC( FF_CT_WIN_NO_KILLS, "#funfact_ct_win_no_kills", 0.4f, FFEVAL_CT_WIN_NO_KILLS, GameFlags::NotGunGame );
  1041. DECLARE_FUNFACT_EVALFUNC( FF_T_WIN_NO_KILLS, "#funfact_t_win_no_kills", 0.4f, FFEVAL_T_WIN_NO_KILLS, GameFlags::NotGunGame );
  1042. DECLARE_FUNFACT_EVALFUNC( FF_T_WIN_NO_CASUALTIES, "#funfact_t_win_no_casualties", 0.2f, FFEVAL_T_WIN_NO_CASUALTIES, GameFlags::NotGunGame );
  1043. DECLARE_FUNFACT_EVALFUNC( FF_CT_WIN_NO_CASUALTIES, "#funfact_ct_win_no_casualties", 0.2f, FFEVAL_CT_WIN_NO_CASUALTIES, GameFlags::NotGunGame );
  1044. DECLARE_FUNFACT_EVALFUNC( FF_FIRST_KILL, "#funfact_first_kill", 0.2f, FFEVAL_FIRST_KILL, GameFlags::NotGunGame );
  1045. DECLARE_FUNFACT_EVALFUNC( FF_FIRST_BLOOD, "#funfact_first_blood", 0.2f, FFEVAL_FIRST_BLOOD, GameFlags::NotGunGame );
  1046. DECLARE_FUNFACT_EVALFUNC( FF_WON_AS_LAST_MEMBER, "#funfact_won_as_last_member", 0.6f, FFEVAL_WON_AS_LAST_MEMBER, GameFlags::NotGunGame );
  1047. DECLARE_FUNFACT_EVALFUNC( FF_SHORT_ROUND, "#funfact_short_round", 0.2f, FFEVAL_SHORT_ROUND, GameFlags::Classic );
  1048. DECLARE_FUNFACT_EVALFUNC( FF_T_ACCURACY, "#funfact_terrorist_accuracy", 0.1f, FFEVAL_T_ACCURACY, GameFlags::AllModes );
  1049. DECLARE_FUNFACT_EVALFUNC( FF_CT_ACCURACY, "#funfact_ct_accuracy", 0.1f, FFEVAL_CT_ACCURACY, GameFlags::AllModes );
  1050. DECLARE_FUNFACT_EVALFUNC( FF_BEST_T_ACCURACY, "#funfact_best_terrorist_accuracy", 0.1f, FFEVAL_BEST_TERRORIST_ACCURACY, GameFlags::AllModes );
  1051. DECLARE_FUNFACT_EVALFUNC( FF_BEST_CT_ACCURACY, "#funfact_best_counterterrorist_accuracy", 0.1f, FFEVAL_BEST_COUNTERTERRORIST_ACCURACY, GameFlags::AllModes );
  1052. DECLARE_FUNFACT_EVALFUNC( FF_KNIFE_LEVEL_REACHED, "#funfact_knife_level_reached", 0.3f, FFEVAL_KNIFE_LEVEL_REACHED, GameFlags::GunGame );
  1053. DECLARE_FUNFACT_EVALFUNC( FF_TICKING_TIME, "#funfact_ticking_time", 0.3f, FFEVAL_TICKING_TIME, GameFlags::Demolition );
  1054. DECLARE_FUNFACT_EVALFUNC( FF_CT_WIN_TIME, "#funfact_ct_win_time", 0.2f, FFEVAL_CT_WIN_TIME , GameFlags::NotGunGame | GameFlags::Elimination );
  1055. DECLARE_FUNFACT_EVALFUNC( FF_TER_WIN_TIME, "#funfact_ter_win_time", 0.2f, FFEVAL_TER_WIN_TIME, GameFlags::NotGunGame | GameFlags::Elimination );
  1056. DECLARE_FUNFACT_EVALFUNC( FF_PICKUP_BOMB, "#funfact_pickup_bomb", 0.3f, FFEVAL_PICKUP_BOMB, GameFlags::Demolition );
  1057. DECLARE_FUNFACT_EVALFUNC( FF_BOMB_PLANTED_BEFORE_KILL,"#funfact_bomb_planted_before_kill", 0.3f, FFEVAL_BOMB_PLANTED_BEFORE_KILL, GameFlags::Demolition );
  1058. DECLARE_FUNFACT_EVALFUNC( FF_FAILED_BOMB_PLANTS, "#funfact_failed_bomb_plants", 0.3f, FFEVAL_FAILED_BOMB_PLANTS, GameFlags::Demolition );
  1059. DECLARE_FUNFACT_EVALFUNC( FF_FALLBACK, "", 0.0f, FFEVAL_ALWAYS_TRUE, GameFlags::AllModes );