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.

793 lines
29 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. #include "cbase.h"
  3. #include "cs_gamerules.h"
  4. #include "cs_gamestats.h"
  5. #include "funfactmgr_cs.h"
  6. #include "funfact_cs.h"
  7. #include "../../game/shared/cstrike/weapon_csbase.h"
  8. #include "cs_achievement_constants.h"
  9. #define FIRST_BLOOD_TIME 45.0f
  10. #define FIRST_KILL_TIME 45.0f
  11. #define SHORT_ROUND_TIME 30.0f
  12. #define MIN_SHOTS_FOR_ACCURACY 10
  13. enum FunFactId
  14. {
  15. FUNFACT_CT_WIN_NO_KILLS,
  16. FUNFACT_T_WIN_NO_KILLS,
  17. FUNFACT_KILL_DEFUSER,
  18. FUNFACT_KILL_RESCUER,
  19. FUNFACT_T_WIN_NO_CASUALTIES,
  20. FUNFACT_CT_WIN_NO_CASUALTIES,
  21. FUNFACT_DAMAGE_WITH_GRENADES,
  22. FUNFACT_KILLS_WITH_GRENADES,
  23. FUNFACT_KILLS_WITH_SINGLE_GRENADE,
  24. FUNFACT_DAMAGE_NO_KILLS,
  25. FUNFACT_KILLED_ENEMIES,
  26. FUNFACT_FIRST_KILL,
  27. FUNFACT_FIRST_BLOOD,
  28. FUNFACT_SHORT_ROUND,
  29. FUNFACT_BEST_ACCURACY,
  30. FUNFACT_KNIFE_KILLS,
  31. FUNFACT_BLIND_KILLS,
  32. FUNFACT_KILLS_WITH_LAST_ROUND,
  33. FUNFACT_DONATED_WEAPONS,
  34. FUNFACT_POSTHUMOUS_KILLS_WITH_GRENADE,
  35. FUNFACT_KNIFE_IN_GUNFIGHT,
  36. FUNFACT_NUM_TIMES_JUMPED,
  37. FUNFACT_FALL_DAMAGE,
  38. FUNFACT_ITEMS_PURCHASED,
  39. FUNFACT_WON_AS_LAST_MEMBER,
  40. FUNFACT_NUMBER_OF_OVERKILLS,
  41. FUNFACT_SHOTS_FIRED,
  42. FUNFACT_MONEY_SPENT,
  43. FUNFACT_SURVIVED_MULTIPLE_ATTACKERS,
  44. FUNFACT_DIED_FROM_MULTIPLE_ATTACKERS,
  45. FUNFACT_DAMAGE_MULTIPLE_ENEMIES,
  46. FUNFACT_GRENADES_THROWN,
  47. FUNFACT_USED_ALL_AMMO,
  48. FUNFACT_DEFENDED_BOMB,
  49. FUNFACT_ITEMS_DROPPED_VALUE,
  50. FUNFACT_KILL_WOUNDED_ENEMIES,
  51. FUNFACT_USED_MULTIPLE_WEAPONS,
  52. FUNFACT_TERRORIST_ACCURACY,
  53. FUNFACT_CT_ACCURACY,
  54. FUNFACT_SAME_UNIFORM_TERRORIST,
  55. FUNFACT_SAME_UNIFORM_CT,
  56. FUNFACT_BEST_TERRORIST_ACCURACY,
  57. FUNFACT_BEST_COUNTERTERRORIST_ACCURACY,
  58. FUNFACT_FALLBACK1,
  59. FUNFACT_FALLBACK2,
  60. FUNFACT_KILLS_HEADSHOTS,
  61. FUNFACT_BROKE_WINDOWS,
  62. FUNFACT_NIGHTVISION_DAMAGE,
  63. FUNFACT_DEFUSED_WITH_DROPPED_KIT,
  64. FUNFACT_KILLED_HALF_OF_ENEMIES,
  65. };
  66. CFunFactHelper *CFunFactHelper::s_pFirst = NULL;
  67. //=============================================================================
  68. // Generic evaluation Fun Fact
  69. // This fun fact will evaluate the specified function to determine when it is
  70. // valid. This is basically just a glue class for simple evaluation functions.
  71. //=============================================================================
  72. // 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
  73. typedef bool (*fFunFactEval)( int &iPlayer, int &data1, int &data2, int &data3 );
  74. class CFunFact_GenericEvalFunction : public FunFactEvaluator
  75. {
  76. public:
  77. CFunFact_GenericEvalFunction(FunFactId id, const char* szLocalizationToken, float fCoolness, fFunFactEval pfnEval ) :
  78. FunFactEvaluator(id, szLocalizationToken, fCoolness),
  79. m_pfnEval(pfnEval)
  80. {}
  81. virtual bool Evaluate( FunFactVector& results ) const
  82. {
  83. FunFact funfact;
  84. if (m_pfnEval(funfact.iPlayer, funfact.iData1, funfact.iData2, funfact.iData3))
  85. {
  86. funfact.id = GetId();
  87. funfact.szLocalizationToken = GetLocalizationToken();
  88. funfact.fMagnitude = 0.0f;
  89. results.AddToTail(funfact);
  90. return true;
  91. }
  92. else
  93. return false;
  94. }
  95. private:
  96. fFunFactEval m_pfnEval;
  97. };
  98. #define DECLARE_FUNFACT_EVALFUNC(funfactId, szLocalizationToken, fCoolness, pfnEval) \
  99. static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
  100. { \
  101. return new CFunFact_GenericEvalFunction(funfactId, szLocalizationToken, fCoolness, pfnEval);\
  102. }; \
  103. static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
  104. //=============================================================================
  105. // Per-player evaluation Fun Fact
  106. // Evaluate the function per player and generate a fun fact for each valid or
  107. // highest valid player
  108. //=============================================================================
  109. namespace EvalFlags
  110. {
  111. enum Type
  112. {
  113. All = 0x00,
  114. TeamCT = 0x01,
  115. TeamTerrorist = 0x02,
  116. HighestOnly = 0x04, // when not set, generates fun facts for all valid testees
  117. Alive = 0x08,
  118. Dead = 0x10,
  119. WinningTeam = 0x20,
  120. LosingTeam = 0x40,
  121. };
  122. };
  123. bool PlayerQualifies( const CBasePlayer* pPlayer, int flags )
  124. {
  125. if ( (flags & EvalFlags::TeamCT) && pPlayer->GetTeamNumber() != TEAM_CT )
  126. return false;
  127. if ( (flags & EvalFlags::TeamTerrorist) && pPlayer->GetTeamNumber() != TEAM_TERRORIST )
  128. return false;
  129. if ( (flags & EvalFlags::Dead) && const_cast<CBasePlayer*>(pPlayer)->IsAlive() ) // IsAlive() really isn't const correct
  130. return false;
  131. if ( (flags & EvalFlags::Alive) && !const_cast<CBasePlayer*>(pPlayer)->IsAlive() )
  132. return false;
  133. if ( (flags & EvalFlags::WinningTeam) && pPlayer->GetTeamNumber() != CSGameRules()->m_iRoundWinStatus )
  134. return false;
  135. if ( (flags & EvalFlags::LosingTeam) && pPlayer->GetTeamNumber() == CSGameRules()->m_iRoundWinStatus )
  136. return false;
  137. return true;
  138. }
  139. typedef int (*PlayerEvalFunction)(CCSPlayer* pPlayer);
  140. class CFunFact_PlayerEvalFunction : public FunFactEvaluator
  141. {
  142. public:
  143. CFunFact_PlayerEvalFunction(FunFactId id, const char* szLocalizationToken, float fCoolness, PlayerEvalFunction pfnEval,
  144. int iMin, int flags ) :
  145. FunFactEvaluator(id, szLocalizationToken, fCoolness),
  146. m_pfnEval(pfnEval),
  147. m_min(iMin),
  148. m_flags(flags)
  149. {}
  150. virtual bool Evaluate( FunFactVector& results ) const
  151. {
  152. int iBestValue = 0;
  153. int iBestPlayer = 0;
  154. bool bResult = false;
  155. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  156. {
  157. CCSPlayer* pPlayer = ToCSPlayer(UTIL_PlayerByIndex( i ) );
  158. if ( pPlayer )
  159. {
  160. if (!PlayerQualifies(pPlayer, m_flags))
  161. continue;
  162. int iValue = m_pfnEval(pPlayer);
  163. if (m_flags & EvalFlags::HighestOnly)
  164. {
  165. if ( iValue > iBestValue )
  166. {
  167. iBestValue = iValue;
  168. iBestPlayer = i;
  169. }
  170. }
  171. else
  172. {
  173. // generate fun facts for any player who meets the validation requirement
  174. if ( iValue >= m_min )
  175. {
  176. FunFact funfact;
  177. funfact.id = GetId();
  178. funfact.szLocalizationToken = GetLocalizationToken();
  179. funfact.iPlayer = i;
  180. funfact.iData1 = iValue;
  181. funfact.fMagnitude = 1.0f - ((float)m_min / iValue);
  182. results.AddToTail(funfact);
  183. bResult = true;
  184. }
  185. }
  186. }
  187. }
  188. if ( (m_flags & EvalFlags::HighestOnly) && iBestValue >= m_min )
  189. {
  190. FunFact funfact;
  191. funfact.id = GetId();
  192. funfact.szLocalizationToken = GetLocalizationToken();
  193. funfact.iPlayer = iBestPlayer;
  194. funfact.iData1 = iBestValue;
  195. funfact.fMagnitude = 1.0f - ((float)m_min / iBestValue);
  196. results.AddToTail(funfact);
  197. bResult = true;
  198. }
  199. return bResult;
  200. }
  201. private:
  202. PlayerEvalFunction m_pfnEval;
  203. int m_min;
  204. int m_flags;
  205. };
  206. #define DECLARE_FUNFACT_PLAYERFUNC(funfactId, szLocalizationToken, fCoolness, pfnEval, iMin, iFlags) \
  207. static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
  208. { \
  209. return new CFunFact_PlayerEvalFunction(funfactId, szLocalizationToken, fCoolness, pfnEval, iMin, iFlags); \
  210. }; \
  211. static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
  212. //=============================================================================
  213. // Per-team evaluation Fun Fact
  214. //=============================================================================
  215. typedef bool (*TeamEvalFunction)(int iTeam, int &data1, int &data2, int &data3);
  216. class CFunFact_TeamEvalFunction : public FunFactEvaluator
  217. {
  218. public:
  219. CFunFact_TeamEvalFunction(FunFactId id, const char* szLocalizationToken, float fCoolness, TeamEvalFunction pfnEval, int iTeam ) :
  220. FunFactEvaluator(id, szLocalizationToken, fCoolness),
  221. m_pfnEval(pfnEval),
  222. m_team(iTeam)
  223. {}
  224. virtual bool Evaluate( FunFactVector& results ) const
  225. {
  226. int iData1, iData2, iData3;
  227. if ( m_pfnEval(m_team, iData1, iData2, iData3) )
  228. {
  229. FunFact funfact;
  230. funfact.id = GetId();
  231. funfact.szLocalizationToken = GetLocalizationToken();
  232. funfact.fMagnitude = 0.0f;
  233. results.AddToTail(funfact);
  234. return true;
  235. }
  236. return false;
  237. }
  238. private:
  239. TeamEvalFunction m_pfnEval;
  240. int m_team;
  241. };
  242. #define DECLARE_FUNFACT_TEAMFUNC(funfactId, szLocalizationToken, fCoolness, pfnEval, iTeam) \
  243. static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
  244. { \
  245. return new CFunFact_TeamEvalFunction(funfactId, szLocalizationToken, fCoolness, pfnEval, iTeam);\
  246. }; \
  247. static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
  248. //=============================================================================
  249. // High Stat-based Fun Fact
  250. // This fun fact will find the player with the highest value for a particular
  251. // stat, and validate when that stat exceeds a specified minimum
  252. //=============================================================================
  253. class CFunFact_StatBest : public FunFactEvaluator
  254. {
  255. public:
  256. CFunFact_StatBest(FunFactId id, const char* szLocalizationToken, float fCoolness, CSStatType_t statId, int iMin, int flags ) :
  257. FunFactEvaluator(id, szLocalizationToken, fCoolness),
  258. m_statId(statId),
  259. m_min(iMin),
  260. m_flags(flags)
  261. {
  262. V_strncpy(m_singularLocalizationToken, szLocalizationToken, sizeof(m_singularLocalizationToken));
  263. if (m_min == 1)
  264. {
  265. V_strncat(m_singularLocalizationToken, "_singular", sizeof(m_singularLocalizationToken));
  266. }
  267. }
  268. virtual bool Evaluate( FunFactVector& results ) const
  269. {
  270. int iBestValue = 0;
  271. int iBestPlayer = 0;
  272. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  273. {
  274. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  275. if ( pPlayer )
  276. {
  277. if (!PlayerQualifies(pPlayer, m_flags))
  278. continue;
  279. int iValue = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[m_statId];
  280. if ( iValue > iBestValue )
  281. {
  282. iBestValue = iValue;
  283. iBestPlayer = i;
  284. }
  285. }
  286. }
  287. if ( iBestValue >= m_min )
  288. {
  289. FunFact funfact;
  290. funfact.id = GetId();
  291. funfact.szLocalizationToken = iBestValue == 1 ? m_singularLocalizationToken : GetLocalizationToken();
  292. funfact.iPlayer = iBestPlayer;
  293. funfact.iData1 = iBestValue;
  294. funfact.fMagnitude = 1.0f - ((float)m_min / iBestValue);
  295. results.AddToTail(funfact);
  296. return true;
  297. }
  298. return false;
  299. }
  300. private:
  301. CSStatType_t m_statId;
  302. int m_min;
  303. char m_singularLocalizationToken[128];
  304. int m_flags;
  305. };
  306. #define DECLARE_FUNFACT_STATBEST(funfactId, szLocalizationToken, fCoolness, statId, iMin, flags) \
  307. static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
  308. { \
  309. return new CFunFact_StatBest(funfactId, szLocalizationToken, fCoolness, statId, iMin, flags); \
  310. }; \
  311. static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
  312. //=============================================================================
  313. // Sum-based Fun Fact
  314. // This fun fact will add up a stat for all players, and is valid when the
  315. // sum exceeds a threshold
  316. //=============================================================================
  317. class CFunFact_StatSum : public FunFactEvaluator
  318. {
  319. public:
  320. CFunFact_StatSum(FunFactId id, const char* szLocalizationToken, float fCoolness, CSStatType_t statId, int iMin, EvalFlags::Type flags ) :
  321. FunFactEvaluator(id, szLocalizationToken, fCoolness),
  322. m_statId(statId),
  323. m_min(iMin),
  324. m_flags(flags)
  325. {}
  326. virtual bool Evaluate( FunFactVector& results ) const
  327. {
  328. int iSum = 0;
  329. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  330. {
  331. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  332. if ( pPlayer )
  333. {
  334. if (!PlayerQualifies(pPlayer, m_flags))
  335. continue;
  336. iSum += CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[m_statId];
  337. }
  338. }
  339. if ( iSum >= m_min )
  340. {
  341. FunFact funfact;
  342. funfact.id = GetId();
  343. funfact.szLocalizationToken = GetLocalizationToken();
  344. funfact.iPlayer = 0;
  345. funfact.iData1 = iSum;
  346. funfact.fMagnitude = 1.0f - ((float)m_min / iSum);
  347. results.AddToTail(funfact);
  348. return true;
  349. }
  350. return false;
  351. }
  352. private:
  353. CSStatType_t m_statId;
  354. int m_min;
  355. int m_flags;
  356. };
  357. #define DECLARE_FUNFACT_STATSUM(funfactId, szLocalizationToken, fCoolness, statId, iMin, flags) \
  358. static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
  359. { \
  360. return new CFunFact_StatSum(funfactId, szLocalizationToken, fCoolness, statId, iMin, flags); \
  361. }; \
  362. static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
  363. //=============================================================================
  364. // Helper function to calculate team accuracy
  365. //=============================================================================
  366. float GetTeamAccuracy( int teamNumber )
  367. {
  368. int teamShots = 0;
  369. int teamHits = 0;
  370. //Add up hits and shots
  371. CBasePlayer *pPlayer = NULL;
  372. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  373. {
  374. pPlayer = UTIL_PlayerByIndex( i );
  375. if (pPlayer)
  376. {
  377. if (pPlayer->GetTeamNumber() == teamNumber)
  378. {
  379. teamShots += CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_FIRED];;
  380. teamHits += CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_HIT];;
  381. }
  382. }
  383. }
  384. if (teamShots > MIN_SHOTS_FOR_ACCURACY)
  385. return (float)teamHits / teamShots;
  386. return 0.0f;
  387. }
  388. //=============================================================================
  389. // fun fact explicit evaluation functions
  390. //=============================================================================
  391. bool FFEVAL_ALWAYS_TRUE( int &iPlayer, int &data1, int &data2, int &data3 )
  392. {
  393. return true;
  394. }
  395. bool FFEVAL_CT_WIN_NO_KILLS( int &iPlayer, int &data1, int &data2, int &data3 )
  396. {
  397. return ( CSGameRules()->m_iRoundWinStatus == WINNER_CT && CSGameRules()->m_bNoTerroristsKilled );
  398. }
  399. bool FFEVAL_T_WIN_NO_KILLS( int &iPlayer, int &data1, int &data2, int &data3 )
  400. {
  401. return ( CSGameRules()->m_iRoundWinStatus == WINNER_TER && CSGameRules()->m_bNoCTsKilled );
  402. }
  403. bool FFEVAL_T_WIN_NO_CASUALTIES( int &iPlayer, int &data1, int &data2, int &data3 )
  404. {
  405. return ( CSGameRules()->m_iRoundWinStatus == WINNER_TER && CSGameRules()->m_bNoTerroristsKilled );
  406. }
  407. bool FFEVAL_CT_WIN_NO_CASUALTIES( int &iPlayer, int &data1, int &data2, int &data3 )
  408. {
  409. return ( CSGameRules()->m_iRoundWinStatus == WINNER_CT && CSGameRules()->m_bNoCTsKilled );
  410. }
  411. int FFEVAL_KILLED_DEFUSER( CCSPlayer* pPlayer )
  412. {
  413. return pPlayer->GetKilledDefuser() ? 1 : 0;
  414. }
  415. int FFEVAL_KILLED_RESCUER( CCSPlayer* pPlayer )
  416. {
  417. return pPlayer->GetKilledRescuer() ? 1 : 0;
  418. }
  419. int FFEVAL_KILLS_WITH_GRENADE( CCSPlayer* pPlayer )
  420. {
  421. return pPlayer->GetMaxGrenadeKills();
  422. }
  423. int FFEVAL_DAMAGE_NO_KILLS( CCSPlayer* pPlayer )
  424. {
  425. if (CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_KILLS] == 0)
  426. return CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_DAMAGE];
  427. else
  428. return 0;
  429. }
  430. int FFEVAL_FIRST_KILL( CCSPlayer* pPlayer )
  431. {
  432. if ( pPlayer == CSGameRules()->m_pFirstKill && CSGameRules()->m_firstKillTime < FIRST_KILL_TIME )
  433. return CSGameRules()->m_firstKillTime;
  434. else
  435. return 0;
  436. }
  437. int FFEVAL_FIRST_BLOOD( CCSPlayer* pPlayer )
  438. {
  439. if ( pPlayer == CSGameRules()->m_pFirstBlood && CSGameRules()->m_firstBloodTime < FIRST_BLOOD_TIME )
  440. return CSGameRules()->m_firstBloodTime;
  441. else
  442. return 0;
  443. }
  444. bool FFEVAL_SHORT_ROUND( int &iPlayer, int &data1, int &data2, int &data3 )
  445. {
  446. if ( CSGameRules()->GetRoundLength() - CSGameRules()->GetRoundRemainingTime() < SHORT_ROUND_TIME )
  447. {
  448. data1 = CSGameRules()->GetRoundLength() - CSGameRules()->GetRoundRemainingTime();
  449. return true;
  450. }
  451. return false;
  452. }
  453. int FFEVAL_ACCURACY( CCSPlayer* pPlayer )
  454. {
  455. float shots = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_FIRED];
  456. float hits = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_HIT];
  457. if (shots >= MIN_SHOTS_FOR_ACCURACY)
  458. return RoundFloatToInt(100.0f * hits / shots);
  459. return 0;
  460. }
  461. int FFEVAL_KILLED_HALF_OF_ENEMIES( CCSPlayer* pPlayer )
  462. {
  463. return pPlayer->GetPercentageOfEnemyTeamKilled();
  464. }
  465. bool FFEVAL_WON_AS_LAST_MEMBER( int &iPlayer, int &data1, int &data2, int &data3 )
  466. {
  467. CCSPlayer *pCSPlayer = NULL;
  468. int winningTeam = CSGameRules()->m_iRoundWinStatus;
  469. if (winningTeam != TEAM_TERRORIST && winningTeam != TEAM_CT)
  470. {
  471. return false;
  472. }
  473. int losingTeam = (winningTeam == TEAM_TERRORIST) ? TEAM_CT : TEAM_TERRORIST;
  474. CCSGameRules::TeamPlayerCounts playerCounts[TEAM_MAXCOUNT];
  475. CSGameRules()->GetPlayerCounts(playerCounts);
  476. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  477. {
  478. pCSPlayer = ToCSPlayer(UTIL_PlayerByIndex( i ) );
  479. if( pCSPlayer && pCSPlayer->GetTeamNumber() == winningTeam && pCSPlayer->IsAlive())
  480. {
  481. //Check if the player is still the only living member of his team ( on the off chance that a player joins late)
  482. //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,
  483. //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)
  484. if (playerCounts[winningTeam].totalAlivePlayers == 1 && playerCounts[losingTeam].totalAlivePlayers == 0)
  485. {
  486. const PlayerStats_t& playerStats = CCS_GameStats.FindPlayerStats( pCSPlayer );
  487. iPlayer = i;
  488. data1 = playerStats.statsCurrentRound[CSSTAT_KILLS_WHILE_LAST_PLAYER_ALIVE];
  489. if (data1 >= 2)
  490. {
  491. return true;
  492. }
  493. }
  494. }
  495. }
  496. return false;
  497. }
  498. int FFEVAL_KNIFE_IN_GUNFIGHT( CCSPlayer* pPlayer )
  499. {
  500. return pPlayer->WasWieldingKnifeAndKilledByGun() ? 1 : 0;
  501. }
  502. int FFEVAL_MULTIPLE_ATTACKER_COUNT( CCSPlayer* pPlayer )
  503. {
  504. return pPlayer->GetNumEnemyDamagers();
  505. }
  506. int FFEVAL_USED_ALL_AMMO( CCSPlayer* pPlayer )
  507. {
  508. CWeaponCSBase *pRifleWeapon = dynamic_cast< CWeaponCSBase * >(pPlayer->Weapon_GetSlot( WEAPON_SLOT_RIFLE ));
  509. CWeaponCSBase *pHandgunWeapon = dynamic_cast< CWeaponCSBase * >(pPlayer->Weapon_GetSlot( WEAPON_SLOT_PISTOL ));
  510. if ( pRifleWeapon && !pRifleWeapon->HasAmmo() && pHandgunWeapon && !pHandgunWeapon->HasAmmo() )
  511. return 1;
  512. else
  513. return 0;
  514. }
  515. int FFEVAL_DAMAGE_MULTIPLE_ENEMIES( CCSPlayer* pPlayer )
  516. {
  517. return pPlayer->GetNumEnemiesDamaged();
  518. }
  519. int FFEVAL_USED_MULTIPLE_WEAPONS( CCSPlayer* pPlayer )
  520. {
  521. return pPlayer->GetNumFirearmsUsed();
  522. }
  523. int FFEVAL_DEFUSED_WITH_DROPPED_KIT( CCSPlayer* pPlayer )
  524. {
  525. return pPlayer->GetDefusedWithPickedUpKit() ? 1 : 0;
  526. }
  527. bool FFEVAL_TERRORIST_ACCURACY( int &iPlayer, int &data1, int &data2, int &data3 )
  528. {
  529. float terroristAccuracy = GetTeamAccuracy(TEAM_TERRORIST);
  530. float ctAccuracy = GetTeamAccuracy(TEAM_CT);
  531. if (terroristAccuracy > 0.2f && terroristAccuracy > ctAccuracy)
  532. {
  533. data1 = RoundFloatToInt(terroristAccuracy * 100.0f);
  534. return true;
  535. }
  536. return false;
  537. }
  538. bool FFEVAL_CT_ACCURACY( int &iPlayer, int &data1, int &data2, int &data3 )
  539. {
  540. float terroristAccuracy = GetTeamAccuracy(TEAM_TERRORIST);
  541. float ctAccuracy = GetTeamAccuracy(TEAM_CT);
  542. if (ctAccuracy > 0.2f && ctAccuracy > terroristAccuracy)
  543. {
  544. data1 = RoundFloatToInt(ctAccuracy * 100.0f);
  545. return true;
  546. }
  547. return false;
  548. }
  549. bool FFEVAL_SAME_UNIFORM( int iTeam, int &iData1, int &iData2, int &iData3 )
  550. {
  551. int numberInUniform = 0;
  552. int iUniform = -1;
  553. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  554. {
  555. CCSPlayer *pCSPlayer = ToCSPlayer(UTIL_PlayerByIndex( i ) );
  556. if ( pCSPlayer && pCSPlayer->GetTeamNumber() == iTeam && pCSPlayer->State_Get() != STATE_PICKINGCLASS)
  557. {
  558. if (iUniform == -1)
  559. {
  560. iUniform = pCSPlayer->PlayerClass();
  561. }
  562. else if (pCSPlayer->PlayerClass() != iUniform)
  563. {
  564. return false;
  565. }
  566. ++numberInUniform;
  567. }
  568. }
  569. return numberInUniform >= 3;
  570. }
  571. bool FFEVAL_BEST_TERRORIST_ACCURACY( int &iPlayer, int &data1, int &data2, int &data3 )
  572. {
  573. float fAccuracy = 0.0f, fBestAccuracy = 0.0f;
  574. CBasePlayer *pPlayer = NULL;
  575. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  576. {
  577. pPlayer = UTIL_PlayerByIndex( i );
  578. // Look only at terrorist players
  579. if ( pPlayer && pPlayer->GetTeamNumber() == TEAM_TERRORIST )
  580. {
  581. // Calculate accuracy the terrorist
  582. float shots = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_FIRED];
  583. float hits = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_HIT];
  584. if (shots > MIN_SHOTS_FOR_ACCURACY)
  585. {
  586. fAccuracy = (float)hits / shots;
  587. }
  588. // Track the most accurate terrorist
  589. if ( fAccuracy > fBestAccuracy )
  590. {
  591. fBestAccuracy = fAccuracy;
  592. iPlayer = i;
  593. }
  594. }
  595. }
  596. if ( fBestAccuracy - GetTeamAccuracy( TEAM_TERRORIST ) >= 0.10f )
  597. {
  598. data1 = RoundFloatToInt(fBestAccuracy * 100.0f);
  599. data2 = RoundFloatToInt(GetTeamAccuracy( TEAM_TERRORIST ) * 100.0f);
  600. return true;
  601. }
  602. return false;
  603. }
  604. bool FFEVAL_BEST_COUNTERTERRORIST_ACCURACY( int &iPlayer, int &data1, int &data2, int &data3 )
  605. {
  606. float fAccuracy = 0.0f, fBestAccuracy = 0.0f;
  607. CBasePlayer *pPlayer = NULL;
  608. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  609. {
  610. pPlayer = UTIL_PlayerByIndex( i );
  611. // Look only at counter-terrorist players
  612. if ( pPlayer && pPlayer->GetTeamNumber() == TEAM_CT )
  613. {
  614. // Calculate accuracy the counter-terrorist
  615. float shots = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_FIRED];
  616. float hits = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_HIT];
  617. if (shots > MIN_SHOTS_FOR_ACCURACY)
  618. {
  619. fAccuracy = (float)hits / shots;
  620. }
  621. // Track the most accurate counter-terrorist
  622. if ( fAccuracy > fBestAccuracy )
  623. {
  624. fBestAccuracy = fAccuracy;
  625. iPlayer = i;
  626. }
  627. }
  628. }
  629. if ( fBestAccuracy - GetTeamAccuracy( TEAM_CT ) >= 0.10f )
  630. {
  631. data1 = RoundFloatToInt(fBestAccuracy * 100.0f);
  632. data2 = RoundFloatToInt(GetTeamAccuracy( TEAM_CT ) * 100.0f);
  633. return true;
  634. }
  635. return false;
  636. }
  637. //=============================================================================
  638. // Fun Fact Declarations
  639. //=============================================================================
  640. DECLARE_FUNFACT_STATBEST( FUNFACT_DAMAGE_WITH_GRENADES, "#funfact_damage_with_grenade", 0.5f, CSSTAT_GRENADE_DAMAGE, 200, EvalFlags::HighestOnly);
  641. DECLARE_FUNFACT_STATBEST( FUNFACT_KNIFE_KILLS, "#funfact_knife_kills", 0.5f, CSSTAT_KILLS_KNIFE, 1, EvalFlags::HighestOnly);
  642. DECLARE_FUNFACT_STATBEST( FUNFACT_KILLS_WITH_GRENADES, "#funfact_kills_grenades", 0.7f, CSSTAT_KILLS_HEGRENADE, 2, EvalFlags::HighestOnly);
  643. DECLARE_FUNFACT_STATBEST( FUNFACT_BLIND_KILLS, "#funfact_blind_kills", 0.9f, CSSTAT_KILLS_WHILE_BLINDED, 1, EvalFlags::HighestOnly);
  644. DECLARE_FUNFACT_STATBEST( FUNFACT_KILLED_ENEMIES, "#funfact_killed_enemies", 0.6f, CSSTAT_KILLS, 3, EvalFlags::HighestOnly);
  645. DECLARE_FUNFACT_STATBEST( FUNFACT_KILLS_WITH_LAST_ROUND, "#funfact_kills_with_last_round", 0.6f, CSSTAT_KILLS_WITH_LAST_ROUND, 1, EvalFlags::HighestOnly);
  646. DECLARE_FUNFACT_STATBEST( FUNFACT_DONATED_WEAPONS, "#funfact_donated_weapons", 0.3f, CSSTAT_WEAPONS_DONATED, 2, EvalFlags::HighestOnly);
  647. DECLARE_FUNFACT_STATBEST( FUNFACT_NUM_TIMES_JUMPED, "#funfact_num_times_jumped", 0.2f, CSSTAT_TOTAL_JUMPS, 10, EvalFlags::HighestOnly);
  648. DECLARE_FUNFACT_STATBEST( FUNFACT_FALL_DAMAGE, "#funfact_fall_damage", 0.2f, CSSTAT_FALL_DAMAGE, 50, EvalFlags::HighestOnly);
  649. DECLARE_FUNFACT_STATBEST( FUNFACT_POSTHUMOUS_KILLS_WITH_GRENADE, "#funfact_posthumous_kills_with_grenade", 1.0f, CSSTAT_GRENADE_POSTHUMOUSKILLS, 1, EvalFlags::HighestOnly);
  650. DECLARE_FUNFACT_STATBEST( FUNFACT_ITEMS_PURCHASED, "#funfact_items_purchased", 0.2f, CSSTAT_ITEMS_PURCHASED, 5, EvalFlags::HighestOnly);
  651. DECLARE_FUNFACT_STATBEST( FUNFACT_NUMBER_OF_OVERKILLS, "#funfact_number_of_overkills", 0.5f, CSSTAT_DOMINATION_OVERKILLS, 2, EvalFlags::HighestOnly);
  652. DECLARE_FUNFACT_STATBEST( FUNFACT_MONEY_SPENT, "#funfact_money_spent", 0.2f, CSSTAT_MONEY_SPENT, 5000, EvalFlags::HighestOnly);
  653. DECLARE_FUNFACT_STATBEST( FUNFACT_GRENADES_THROWN, "#funfact_grenades_thrown", 0.3f, CSSTAT_GRENADES_THROWN, 2, EvalFlags::HighestOnly);
  654. DECLARE_FUNFACT_STATBEST( FUNFACT_DEFENDED_BOMB, "#funfact_defended_bomb", 0.5f, CSSTAT_KILLS_WHILE_DEFENDING_BOMB, 2, EvalFlags::HighestOnly);
  655. DECLARE_FUNFACT_STATBEST( FUNFACT_ITEMS_DROPPED_VALUE, "#funfact_items_dropped_value", 0.5f, CSTAT_ITEMS_DROPPED_VALUE, 10000, EvalFlags::HighestOnly);
  656. DECLARE_FUNFACT_STATBEST( FUNFACT_KILL_WOUNDED_ENEMIES, "#funfact_kill_wounded_enemies", 0.4f, CSSTAT_KILLS_ENEMY_WOUNDED, 3, EvalFlags::HighestOnly);
  657. DECLARE_FUNFACT_STATBEST( FUNFACT_KILLS_HEADSHOTS, "#funfact_kills_headshots", 0.7f, CSSTAT_KILLS_HEADSHOT, 3, EvalFlags::HighestOnly);
  658. DECLARE_FUNFACT_STATBEST( FUNFACT_BROKE_WINDOWS, "#funfact_broke_windows", 0.3f, CSSTAT_NUM_BROKEN_WINDOWS, 5, EvalFlags::HighestOnly);
  659. DECLARE_FUNFACT_STATBEST( FUNFACT_NIGHTVISION_DAMAGE, "#funfact_nightvision_damage", 0.5f, CSSTAT_NIGHTVISION_DAMAGE, 100, EvalFlags::HighestOnly);
  660. DECLARE_FUNFACT_STATSUM( FUNFACT_SHOTS_FIRED, "#funfact_shots_fired", 0.1f, CSSTAT_SHOTS_FIRED, 200, EvalFlags::All);
  661. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_KILL_DEFUSER, "#funfact_kill_defuser", 0.6f, FFEVAL_KILLED_DEFUSER, 1, EvalFlags::TeamTerrorist | EvalFlags::WinningTeam);
  662. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_KILL_RESCUER, "#funfact_kill_rescuer", 0.6f, FFEVAL_KILLED_RESCUER, 1, EvalFlags::TeamTerrorist);
  663. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_KILLS_WITH_SINGLE_GRENADE, "#funfact_kills_with_single_grenade", 0.8f, FFEVAL_KILLS_WITH_GRENADE, 2, EvalFlags::HighestOnly);
  664. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_DAMAGE_NO_KILLS, "#funfact_damage_no_kills", 0.4f, FFEVAL_DAMAGE_NO_KILLS, 200, EvalFlags::HighestOnly);
  665. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_FIRST_KILL, "#funfact_first_kill", 0.2f, FFEVAL_FIRST_KILL, 1, EvalFlags::HighestOnly);
  666. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_FIRST_BLOOD, "#funfact_first_blood", 0.2f, FFEVAL_FIRST_BLOOD, 1, EvalFlags::HighestOnly);
  667. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_BEST_ACCURACY, "#funfact_best_accuracy", 0.4f, FFEVAL_ACCURACY, 20, EvalFlags::HighestOnly);
  668. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_KNIFE_IN_GUNFIGHT, "#funfact_knife_in_gunfight", 0.6f, FFEVAL_KNIFE_IN_GUNFIGHT , 1, EvalFlags::All);
  669. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_SURVIVED_MULTIPLE_ATTACKERS, "#funfact_survived_multiple_attackers", 0.3f, FFEVAL_MULTIPLE_ATTACKER_COUNT, 3, EvalFlags::HighestOnly | EvalFlags::Alive);
  670. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_DIED_FROM_MULTIPLE_ATTACKERS, "#funfact_died_from_multiple_attackers", 0.5f, FFEVAL_MULTIPLE_ATTACKER_COUNT, 3, EvalFlags::HighestOnly | EvalFlags::Dead);
  671. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_USED_ALL_AMMO, "#funfact_used_all_ammo", 0.5f, FFEVAL_USED_ALL_AMMO, 1, EvalFlags::All );
  672. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_DAMAGE_MULTIPLE_ENEMIES, "#funfact_damage_multiple_enemies", 0.5f, FFEVAL_DAMAGE_MULTIPLE_ENEMIES, 3, EvalFlags::HighestOnly);
  673. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_USED_MULTIPLE_WEAPONS, "#funfact_used_multiple_weapons", 0.5f, FFEVAL_USED_MULTIPLE_WEAPONS, 4, EvalFlags::HighestOnly);
  674. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_DEFUSED_WITH_DROPPED_KIT, "#funfact_defused_with_dropped_kit", 0.4f, FFEVAL_DEFUSED_WITH_DROPPED_KIT, 1, EvalFlags::TeamCT);
  675. DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_KILLED_HALF_OF_ENEMIES, "#funfact_killed_half_of_enemies", 0.5f, FFEVAL_KILLED_HALF_OF_ENEMIES, 50, EvalFlags::WinningTeam | EvalFlags::HighestOnly);
  676. DECLARE_FUNFACT_EVALFUNC( FUNFACT_CT_WIN_NO_KILLS, "#funfact_ct_win_no_kills", 0.4f, FFEVAL_CT_WIN_NO_KILLS);
  677. DECLARE_FUNFACT_EVALFUNC( FUNFACT_T_WIN_NO_KILLS, "#funfact_t_win_no_kills", 0.4f, FFEVAL_T_WIN_NO_KILLS );
  678. DECLARE_FUNFACT_EVALFUNC( FUNFACT_T_WIN_NO_CASUALTIES, "#funfact_t_win_no_casualties", 0.2f, FFEVAL_T_WIN_NO_CASUALTIES );
  679. DECLARE_FUNFACT_EVALFUNC( FUNFACT_CT_WIN_NO_CASUALTIES, "#funfact_ct_win_no_casualties", 0.2f, FFEVAL_CT_WIN_NO_CASUALTIES );
  680. DECLARE_FUNFACT_EVALFUNC( FUNFACT_SHORT_ROUND, "#funfact_short_round", 0.3f, FFEVAL_SHORT_ROUND );
  681. DECLARE_FUNFACT_EVALFUNC( FUNFACT_WON_AS_LAST_MEMBER, "#funfact_won_as_last_member", 0.6f, FFEVAL_WON_AS_LAST_MEMBER );
  682. DECLARE_FUNFACT_EVALFUNC( FUNFACT_TERRORIST_ACCURACY, "#funfact_terrorist_accuracy", 0.2f, FFEVAL_TERRORIST_ACCURACY);
  683. DECLARE_FUNFACT_EVALFUNC( FUNFACT_CT_ACCURACY, "#funfact_ct_accuracy", 0.2f, FFEVAL_CT_ACCURACY);
  684. DECLARE_FUNFACT_EVALFUNC( FUNFACT_BEST_TERRORIST_ACCURACY, "#funfact_best_terrorist_accuracy", 0.3f, FFEVAL_BEST_TERRORIST_ACCURACY);
  685. DECLARE_FUNFACT_EVALFUNC( FUNFACT_BEST_COUNTERTERRORIST_ACCURACY, "#funfact_best_counterterrorist_accuracy", 0.3f, FFEVAL_BEST_COUNTERTERRORIST_ACCURACY);
  686. DECLARE_FUNFACT_EVALFUNC( FUNFACT_FALLBACK1, "#funfact_fallback1", 0.0f, FFEVAL_ALWAYS_TRUE);
  687. DECLARE_FUNFACT_EVALFUNC( FUNFACT_FALLBACK2, "#funfact_fallback2", 0.0f, FFEVAL_ALWAYS_TRUE);
  688. DECLARE_FUNFACT_TEAMFUNC( FUNFACT_SAME_UNIFORM_TERRORIST, "#funfact_same_uniform_terrorist", 0.5f, FFEVAL_SAME_UNIFORM, TEAM_TERRORIST);
  689. DECLARE_FUNFACT_TEAMFUNC( FUNFACT_SAME_UNIFORM_CT, "#funfact_same_uniform_ct", 0.5f, FFEVAL_SAME_UNIFORM, TEAM_CT);