summaryrefslogtreecommitdiff
path: root/game/server/cstrike/funfact_cs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/cstrike/funfact_cs.cpp')
-rw-r--r--game/server/cstrike/funfact_cs.cpp793
1 files changed, 793 insertions, 0 deletions
diff --git a/game/server/cstrike/funfact_cs.cpp b/game/server/cstrike/funfact_cs.cpp
new file mode 100644
index 0000000..e880359
--- /dev/null
+++ b/game/server/cstrike/funfact_cs.cpp
@@ -0,0 +1,793 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "cbase.h"
+#include "cs_gamerules.h"
+#include "cs_gamestats.h"
+#include "funfactmgr_cs.h"
+#include "funfact_cs.h"
+#include "../../game/shared/cstrike/weapon_csbase.h"
+#include "cs_achievement_constants.h"
+
+#define FIRST_BLOOD_TIME 45.0f
+#define FIRST_KILL_TIME 45.0f
+#define SHORT_ROUND_TIME 30.0f
+#define MIN_SHOTS_FOR_ACCURACY 10
+
+enum FunFactId
+{
+ FUNFACT_CT_WIN_NO_KILLS,
+ FUNFACT_T_WIN_NO_KILLS,
+ FUNFACT_KILL_DEFUSER,
+ FUNFACT_KILL_RESCUER,
+ FUNFACT_T_WIN_NO_CASUALTIES,
+ FUNFACT_CT_WIN_NO_CASUALTIES,
+ FUNFACT_DAMAGE_WITH_GRENADES,
+ FUNFACT_KILLS_WITH_GRENADES,
+ FUNFACT_KILLS_WITH_SINGLE_GRENADE,
+ FUNFACT_DAMAGE_NO_KILLS,
+ FUNFACT_KILLED_ENEMIES,
+ FUNFACT_FIRST_KILL,
+ FUNFACT_FIRST_BLOOD,
+ FUNFACT_SHORT_ROUND,
+ FUNFACT_BEST_ACCURACY,
+ FUNFACT_KNIFE_KILLS,
+ FUNFACT_BLIND_KILLS,
+ FUNFACT_KILLS_WITH_LAST_ROUND,
+ FUNFACT_DONATED_WEAPONS,
+ FUNFACT_POSTHUMOUS_KILLS_WITH_GRENADE,
+ FUNFACT_KNIFE_IN_GUNFIGHT,
+ FUNFACT_NUM_TIMES_JUMPED,
+ FUNFACT_FALL_DAMAGE,
+ FUNFACT_ITEMS_PURCHASED,
+ FUNFACT_WON_AS_LAST_MEMBER,
+ FUNFACT_NUMBER_OF_OVERKILLS,
+ FUNFACT_SHOTS_FIRED,
+ FUNFACT_MONEY_SPENT,
+ FUNFACT_SURVIVED_MULTIPLE_ATTACKERS,
+ FUNFACT_DIED_FROM_MULTIPLE_ATTACKERS,
+ FUNFACT_DAMAGE_MULTIPLE_ENEMIES,
+ FUNFACT_GRENADES_THROWN,
+ FUNFACT_USED_ALL_AMMO,
+ FUNFACT_DEFENDED_BOMB,
+ FUNFACT_ITEMS_DROPPED_VALUE,
+ FUNFACT_KILL_WOUNDED_ENEMIES,
+ FUNFACT_USED_MULTIPLE_WEAPONS,
+ FUNFACT_TERRORIST_ACCURACY,
+ FUNFACT_CT_ACCURACY,
+ FUNFACT_SAME_UNIFORM_TERRORIST,
+ FUNFACT_SAME_UNIFORM_CT,
+ FUNFACT_BEST_TERRORIST_ACCURACY,
+ FUNFACT_BEST_COUNTERTERRORIST_ACCURACY,
+ FUNFACT_FALLBACK1,
+ FUNFACT_FALLBACK2,
+ FUNFACT_KILLS_HEADSHOTS,
+ FUNFACT_BROKE_WINDOWS,
+ FUNFACT_NIGHTVISION_DAMAGE,
+ FUNFACT_DEFUSED_WITH_DROPPED_KIT,
+ FUNFACT_KILLED_HALF_OF_ENEMIES,
+};
+
+
+CFunFactHelper *CFunFactHelper::s_pFirst = NULL;
+
+
+//=============================================================================
+// Generic evaluation Fun Fact
+// This fun fact will evaluate the specified function to determine when it is
+// valid. This is basically just a glue class for simple evaluation functions.
+//=============================================================================
+
+// 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
+typedef bool (*fFunFactEval)( int &iPlayer, int &data1, int &data2, int &data3 );
+
+class CFunFact_GenericEvalFunction : public FunFactEvaluator
+{
+public:
+ CFunFact_GenericEvalFunction(FunFactId id, const char* szLocalizationToken, float fCoolness, fFunFactEval pfnEval ) :
+ FunFactEvaluator(id, szLocalizationToken, fCoolness),
+ m_pfnEval(pfnEval)
+ {}
+
+ virtual bool Evaluate( FunFactVector& results ) const
+ {
+ FunFact funfact;
+ if (m_pfnEval(funfact.iPlayer, funfact.iData1, funfact.iData2, funfact.iData3))
+ {
+ funfact.id = GetId();
+ funfact.szLocalizationToken = GetLocalizationToken();
+ funfact.fMagnitude = 0.0f;
+ results.AddToTail(funfact);
+ return true;
+ }
+ else
+ return false;
+ }
+
+ private:
+ fFunFactEval m_pfnEval;
+};
+#define DECLARE_FUNFACT_EVALFUNC(funfactId, szLocalizationToken, fCoolness, pfnEval) \
+static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
+{ \
+ return new CFunFact_GenericEvalFunction(funfactId, szLocalizationToken, fCoolness, pfnEval);\
+}; \
+static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
+
+
+//=============================================================================
+// Per-player evaluation Fun Fact
+// Evaluate the function per player and generate a fun fact for each valid or
+// highest valid player
+//=============================================================================
+
+namespace EvalFlags
+{
+ enum Type
+ {
+ All = 0x00,
+ TeamCT = 0x01,
+ TeamTerrorist = 0x02,
+ HighestOnly = 0x04, // when not set, generates fun facts for all valid testees
+ Alive = 0x08,
+ Dead = 0x10,
+ WinningTeam = 0x20,
+ LosingTeam = 0x40,
+ };
+};
+
+
+bool PlayerQualifies( const CBasePlayer* pPlayer, int flags )
+{
+ if ( (flags & EvalFlags::TeamCT) && pPlayer->GetTeamNumber() != TEAM_CT )
+ return false;
+ if ( (flags & EvalFlags::TeamTerrorist) && pPlayer->GetTeamNumber() != TEAM_TERRORIST )
+ return false;
+ if ( (flags & EvalFlags::Dead) && const_cast<CBasePlayer*>(pPlayer)->IsAlive() ) // IsAlive() really isn't const correct
+ return false;
+ if ( (flags & EvalFlags::Alive) && !const_cast<CBasePlayer*>(pPlayer)->IsAlive() )
+ return false;
+ if ( (flags & EvalFlags::WinningTeam) && pPlayer->GetTeamNumber() != CSGameRules()->m_iRoundWinStatus )
+ return false;
+ if ( (flags & EvalFlags::LosingTeam) && pPlayer->GetTeamNumber() == CSGameRules()->m_iRoundWinStatus )
+ return false;
+ return true;
+}
+
+
+typedef int (*PlayerEvalFunction)(CCSPlayer* pPlayer);
+
+class CFunFact_PlayerEvalFunction : public FunFactEvaluator
+{
+public:
+ CFunFact_PlayerEvalFunction(FunFactId id, const char* szLocalizationToken, float fCoolness, PlayerEvalFunction pfnEval,
+ int iMin, int flags ) :
+ FunFactEvaluator(id, szLocalizationToken, fCoolness),
+ m_pfnEval(pfnEval),
+ m_min(iMin),
+ m_flags(flags)
+ {}
+
+ virtual bool Evaluate( FunFactVector& results ) const
+ {
+ int iBestValue = 0;
+ int iBestPlayer = 0;
+ bool bResult = false;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer* pPlayer = ToCSPlayer(UTIL_PlayerByIndex( i ) );
+ if ( pPlayer )
+ {
+ if (!PlayerQualifies(pPlayer, m_flags))
+ continue;
+
+ int iValue = m_pfnEval(pPlayer);
+
+ if (m_flags & EvalFlags::HighestOnly)
+ {
+ if ( iValue > iBestValue )
+ {
+ iBestValue = iValue;
+ iBestPlayer = i;
+ }
+ }
+ else
+ {
+ // generate fun facts for any player who meets the validation requirement
+ if ( iValue >= m_min )
+ {
+ FunFact funfact;
+ funfact.id = GetId();
+ funfact.szLocalizationToken = GetLocalizationToken();
+ funfact.iPlayer = i;
+ funfact.iData1 = iValue;
+ funfact.fMagnitude = 1.0f - ((float)m_min / iValue);
+ results.AddToTail(funfact);
+ bResult = true;
+ }
+ }
+ }
+ }
+ if ( (m_flags & EvalFlags::HighestOnly) && iBestValue >= m_min )
+ {
+ FunFact funfact;
+ funfact.id = GetId();
+ funfact.szLocalizationToken = GetLocalizationToken();
+ funfact.iPlayer = iBestPlayer;
+ funfact.iData1 = iBestValue;
+ funfact.fMagnitude = 1.0f - ((float)m_min / iBestValue);
+
+ results.AddToTail(funfact);
+ bResult = true;
+ }
+ return bResult;
+ }
+
+private:
+ PlayerEvalFunction m_pfnEval;
+ int m_min;
+ int m_flags;
+};
+#define DECLARE_FUNFACT_PLAYERFUNC(funfactId, szLocalizationToken, fCoolness, pfnEval, iMin, iFlags) \
+static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
+{ \
+ return new CFunFact_PlayerEvalFunction(funfactId, szLocalizationToken, fCoolness, pfnEval, iMin, iFlags); \
+}; \
+static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
+
+
+//=============================================================================
+// Per-team evaluation Fun Fact
+//=============================================================================
+
+typedef bool (*TeamEvalFunction)(int iTeam, int &data1, int &data2, int &data3);
+
+class CFunFact_TeamEvalFunction : public FunFactEvaluator
+{
+public:
+ CFunFact_TeamEvalFunction(FunFactId id, const char* szLocalizationToken, float fCoolness, TeamEvalFunction pfnEval, int iTeam ) :
+ FunFactEvaluator(id, szLocalizationToken, fCoolness),
+ m_pfnEval(pfnEval),
+ m_team(iTeam)
+ {}
+
+ virtual bool Evaluate( FunFactVector& results ) const
+ {
+ int iData1, iData2, iData3;
+ if ( m_pfnEval(m_team, iData1, iData2, iData3) )
+ {
+ FunFact funfact;
+ funfact.id = GetId();
+ funfact.szLocalizationToken = GetLocalizationToken();
+ funfact.fMagnitude = 0.0f;
+ results.AddToTail(funfact);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ TeamEvalFunction m_pfnEval;
+ int m_team;
+};
+#define DECLARE_FUNFACT_TEAMFUNC(funfactId, szLocalizationToken, fCoolness, pfnEval, iTeam) \
+static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
+{ \
+ return new CFunFact_TeamEvalFunction(funfactId, szLocalizationToken, fCoolness, pfnEval, iTeam);\
+}; \
+static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
+
+
+//=============================================================================
+// High Stat-based Fun Fact
+// This fun fact will find the player with the highest value for a particular
+// stat, and validate when that stat exceeds a specified minimum
+//=============================================================================
+class CFunFact_StatBest : public FunFactEvaluator
+{
+public:
+ CFunFact_StatBest(FunFactId id, const char* szLocalizationToken, float fCoolness, CSStatType_t statId, int iMin, int flags ) :
+ FunFactEvaluator(id, szLocalizationToken, fCoolness),
+ m_statId(statId),
+ m_min(iMin),
+ m_flags(flags)
+ {
+ V_strncpy(m_singularLocalizationToken, szLocalizationToken, sizeof(m_singularLocalizationToken));
+ if (m_min == 1)
+ {
+ V_strncat(m_singularLocalizationToken, "_singular", sizeof(m_singularLocalizationToken));
+ }
+ }
+
+ virtual bool Evaluate( FunFactVector& results ) const
+ {
+ int iBestValue = 0;
+ int iBestPlayer = 0;
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+ if ( pPlayer )
+ {
+ if (!PlayerQualifies(pPlayer, m_flags))
+ continue;
+
+ int iValue = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[m_statId];
+ if ( iValue > iBestValue )
+ {
+ iBestValue = iValue;
+ iBestPlayer = i;
+ }
+ }
+ }
+ if ( iBestValue >= m_min )
+ {
+ FunFact funfact;
+ funfact.id = GetId();
+ funfact.szLocalizationToken = iBestValue == 1 ? m_singularLocalizationToken : GetLocalizationToken();
+ funfact.iPlayer = iBestPlayer;
+ funfact.iData1 = iBestValue;
+ funfact.fMagnitude = 1.0f - ((float)m_min / iBestValue);
+
+ results.AddToTail(funfact);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ CSStatType_t m_statId;
+ int m_min;
+ char m_singularLocalizationToken[128];
+ int m_flags;
+
+};
+#define DECLARE_FUNFACT_STATBEST(funfactId, szLocalizationToken, fCoolness, statId, iMin, flags) \
+static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
+{ \
+ return new CFunFact_StatBest(funfactId, szLocalizationToken, fCoolness, statId, iMin, flags); \
+}; \
+static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
+
+
+//=============================================================================
+// Sum-based Fun Fact
+// This fun fact will add up a stat for all players, and is valid when the
+// sum exceeds a threshold
+//=============================================================================
+class CFunFact_StatSum : public FunFactEvaluator
+{
+public:
+ CFunFact_StatSum(FunFactId id, const char* szLocalizationToken, float fCoolness, CSStatType_t statId, int iMin, EvalFlags::Type flags ) :
+ FunFactEvaluator(id, szLocalizationToken, fCoolness),
+ m_statId(statId),
+ m_min(iMin),
+ m_flags(flags)
+ {}
+
+ virtual bool Evaluate( FunFactVector& results ) const
+ {
+ int iSum = 0;
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+ if ( pPlayer )
+ {
+ if (!PlayerQualifies(pPlayer, m_flags))
+ continue;
+
+ iSum += CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[m_statId];
+ }
+ }
+ if ( iSum >= m_min )
+ {
+ FunFact funfact;
+ funfact.id = GetId();
+ funfact.szLocalizationToken = GetLocalizationToken();
+ funfact.iPlayer = 0;
+ funfact.iData1 = iSum;
+ funfact.fMagnitude = 1.0f - ((float)m_min / iSum);
+
+ results.AddToTail(funfact);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ CSStatType_t m_statId;
+ int m_min;
+ int m_flags;
+
+};
+#define DECLARE_FUNFACT_STATSUM(funfactId, szLocalizationToken, fCoolness, statId, iMin, flags) \
+static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
+{ \
+ return new CFunFact_StatSum(funfactId, szLocalizationToken, fCoolness, statId, iMin, flags); \
+}; \
+static CFunFactHelper g_##funfactId##_Helper( CreateFunFact_##funfactId );
+
+
+
+//=============================================================================
+// Helper function to calculate team accuracy
+//=============================================================================
+
+float GetTeamAccuracy( int teamNumber )
+{
+ int teamShots = 0;
+ int teamHits = 0;
+
+ //Add up hits and shots
+ CBasePlayer *pPlayer = NULL;
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ pPlayer = UTIL_PlayerByIndex( i );
+ if (pPlayer)
+ {
+ if (pPlayer->GetTeamNumber() == teamNumber)
+ {
+ teamShots += CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_FIRED];;
+ teamHits += CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_HIT];;
+ }
+ }
+ }
+
+ if (teamShots > MIN_SHOTS_FOR_ACCURACY)
+ return (float)teamHits / teamShots;
+
+ return 0.0f;
+}
+
+
+
+//=============================================================================
+// fun fact explicit evaluation functions
+//=============================================================================
+
+bool FFEVAL_ALWAYS_TRUE( int &iPlayer, int &data1, int &data2, int &data3 )
+{
+ return true;
+}
+
+bool FFEVAL_CT_WIN_NO_KILLS( int &iPlayer, int &data1, int &data2, int &data3 )
+{
+ return ( CSGameRules()->m_iRoundWinStatus == WINNER_CT && CSGameRules()->m_bNoTerroristsKilled );
+}
+
+bool FFEVAL_T_WIN_NO_KILLS( int &iPlayer, int &data1, int &data2, int &data3 )
+{
+ return ( CSGameRules()->m_iRoundWinStatus == WINNER_TER && CSGameRules()->m_bNoCTsKilled );
+}
+
+bool FFEVAL_T_WIN_NO_CASUALTIES( int &iPlayer, int &data1, int &data2, int &data3 )
+{
+ return ( CSGameRules()->m_iRoundWinStatus == WINNER_TER && CSGameRules()->m_bNoTerroristsKilled );
+}
+
+bool FFEVAL_CT_WIN_NO_CASUALTIES( int &iPlayer, int &data1, int &data2, int &data3 )
+{
+ return ( CSGameRules()->m_iRoundWinStatus == WINNER_CT && CSGameRules()->m_bNoCTsKilled );
+}
+
+int FFEVAL_KILLED_DEFUSER( CCSPlayer* pPlayer )
+{
+ return pPlayer->GetKilledDefuser() ? 1 : 0;
+}
+
+int FFEVAL_KILLED_RESCUER( CCSPlayer* pPlayer )
+{
+ return pPlayer->GetKilledRescuer() ? 1 : 0;
+}
+
+int FFEVAL_KILLS_WITH_GRENADE( CCSPlayer* pPlayer )
+{
+ return pPlayer->GetMaxGrenadeKills();
+}
+
+int FFEVAL_DAMAGE_NO_KILLS( CCSPlayer* pPlayer )
+{
+ if (CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_KILLS] == 0)
+ return CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_DAMAGE];
+ else
+ return 0;
+}
+
+int FFEVAL_FIRST_KILL( CCSPlayer* pPlayer )
+{
+ if ( pPlayer == CSGameRules()->m_pFirstKill && CSGameRules()->m_firstKillTime < FIRST_KILL_TIME )
+ return CSGameRules()->m_firstKillTime;
+ else
+ return 0;
+}
+
+int FFEVAL_FIRST_BLOOD( CCSPlayer* pPlayer )
+{
+ if ( pPlayer == CSGameRules()->m_pFirstBlood && CSGameRules()->m_firstBloodTime < FIRST_BLOOD_TIME )
+ return CSGameRules()->m_firstBloodTime;
+ else
+ return 0;
+}
+
+bool FFEVAL_SHORT_ROUND( int &iPlayer, int &data1, int &data2, int &data3 )
+{
+ if ( CSGameRules()->GetRoundLength() - CSGameRules()->GetRoundRemainingTime() < SHORT_ROUND_TIME )
+ {
+ data1 = CSGameRules()->GetRoundLength() - CSGameRules()->GetRoundRemainingTime();
+ return true;
+ }
+ return false;
+}
+
+int FFEVAL_ACCURACY( CCSPlayer* pPlayer )
+{
+ float shots = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_FIRED];
+ float hits = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_HIT];
+ if (shots >= MIN_SHOTS_FOR_ACCURACY)
+ return RoundFloatToInt(100.0f * hits / shots);
+ return 0;
+}
+
+int FFEVAL_KILLED_HALF_OF_ENEMIES( CCSPlayer* pPlayer )
+{
+ return pPlayer->GetPercentageOfEnemyTeamKilled();
+}
+
+bool FFEVAL_WON_AS_LAST_MEMBER( int &iPlayer, int &data1, int &data2, int &data3 )
+{
+ CCSPlayer *pCSPlayer = NULL;
+ int winningTeam = CSGameRules()->m_iRoundWinStatus;
+
+ if (winningTeam != TEAM_TERRORIST && winningTeam != TEAM_CT)
+ {
+ return false;
+ }
+
+ int losingTeam = (winningTeam == TEAM_TERRORIST) ? TEAM_CT : TEAM_TERRORIST;
+
+ CCSGameRules::TeamPlayerCounts playerCounts[TEAM_MAXCOUNT];
+ CSGameRules()->GetPlayerCounts(playerCounts);
+
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ pCSPlayer = ToCSPlayer(UTIL_PlayerByIndex( i ) );
+ if( pCSPlayer && pCSPlayer->GetTeamNumber() == winningTeam && pCSPlayer->IsAlive())
+ {
+ //Check if the player is still the only living member of his team ( on the off chance that a player joins late)
+ //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,
+ //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)
+ if (playerCounts[winningTeam].totalAlivePlayers == 1 && playerCounts[losingTeam].totalAlivePlayers == 0)
+ {
+ const PlayerStats_t& playerStats = CCS_GameStats.FindPlayerStats( pCSPlayer );
+ iPlayer = i;
+ data1 = playerStats.statsCurrentRound[CSSTAT_KILLS_WHILE_LAST_PLAYER_ALIVE];
+ if (data1 >= 2)
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+int FFEVAL_KNIFE_IN_GUNFIGHT( CCSPlayer* pPlayer )
+{
+ return pPlayer->WasWieldingKnifeAndKilledByGun() ? 1 : 0;
+}
+
+int FFEVAL_MULTIPLE_ATTACKER_COUNT( CCSPlayer* pPlayer )
+{
+ return pPlayer->GetNumEnemyDamagers();
+}
+
+int FFEVAL_USED_ALL_AMMO( CCSPlayer* pPlayer )
+{
+ CWeaponCSBase *pRifleWeapon = dynamic_cast< CWeaponCSBase * >(pPlayer->Weapon_GetSlot( WEAPON_SLOT_RIFLE ));
+ CWeaponCSBase *pHandgunWeapon = dynamic_cast< CWeaponCSBase * >(pPlayer->Weapon_GetSlot( WEAPON_SLOT_PISTOL ));
+ if ( pRifleWeapon && !pRifleWeapon->HasAmmo() && pHandgunWeapon && !pHandgunWeapon->HasAmmo() )
+ return 1;
+ else
+ return 0;
+}
+
+int FFEVAL_DAMAGE_MULTIPLE_ENEMIES( CCSPlayer* pPlayer )
+{
+ return pPlayer->GetNumEnemiesDamaged();
+}
+
+int FFEVAL_USED_MULTIPLE_WEAPONS( CCSPlayer* pPlayer )
+{
+ return pPlayer->GetNumFirearmsUsed();
+}
+
+int FFEVAL_DEFUSED_WITH_DROPPED_KIT( CCSPlayer* pPlayer )
+{
+ return pPlayer->GetDefusedWithPickedUpKit() ? 1 : 0;
+}
+
+bool FFEVAL_TERRORIST_ACCURACY( int &iPlayer, int &data1, int &data2, int &data3 )
+{
+ float terroristAccuracy = GetTeamAccuracy(TEAM_TERRORIST);
+ float ctAccuracy = GetTeamAccuracy(TEAM_CT);
+
+ if (terroristAccuracy > 0.2f && terroristAccuracy > ctAccuracy)
+ {
+ data1 = RoundFloatToInt(terroristAccuracy * 100.0f);
+ return true;
+ }
+ return false;
+}
+
+bool FFEVAL_CT_ACCURACY( int &iPlayer, int &data1, int &data2, int &data3 )
+{
+ float terroristAccuracy = GetTeamAccuracy(TEAM_TERRORIST);
+ float ctAccuracy = GetTeamAccuracy(TEAM_CT);
+
+ if (ctAccuracy > 0.2f && ctAccuracy > terroristAccuracy)
+ {
+ data1 = RoundFloatToInt(ctAccuracy * 100.0f);
+ return true;
+ }
+ return false;
+}
+
+bool FFEVAL_SAME_UNIFORM( int iTeam, int &iData1, int &iData2, int &iData3 )
+{
+ int numberInUniform = 0;
+ int iUniform = -1;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer *pCSPlayer = ToCSPlayer(UTIL_PlayerByIndex( i ) );
+ if ( pCSPlayer && pCSPlayer->GetTeamNumber() == iTeam && pCSPlayer->State_Get() != STATE_PICKINGCLASS)
+ {
+ if (iUniform == -1)
+ {
+ iUniform = pCSPlayer->PlayerClass();
+ }
+ else if (pCSPlayer->PlayerClass() != iUniform)
+ {
+ return false;
+ }
+ ++numberInUniform;
+ }
+ }
+
+ return numberInUniform >= 3;
+}
+
+bool FFEVAL_BEST_TERRORIST_ACCURACY( int &iPlayer, int &data1, int &data2, int &data3 )
+{
+ float fAccuracy = 0.0f, fBestAccuracy = 0.0f;
+ CBasePlayer *pPlayer = NULL;
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ pPlayer = UTIL_PlayerByIndex( i );
+
+ // Look only at terrorist players
+ if ( pPlayer && pPlayer->GetTeamNumber() == TEAM_TERRORIST )
+ {
+ // Calculate accuracy the terrorist
+ float shots = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_FIRED];
+ float hits = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_HIT];
+ if (shots > MIN_SHOTS_FOR_ACCURACY)
+ {
+ fAccuracy = (float)hits / shots;
+ }
+
+ // Track the most accurate terrorist
+ if ( fAccuracy > fBestAccuracy )
+ {
+ fBestAccuracy = fAccuracy;
+ iPlayer = i;
+ }
+ }
+ }
+
+ if ( fBestAccuracy - GetTeamAccuracy( TEAM_TERRORIST ) >= 0.10f )
+ {
+ data1 = RoundFloatToInt(fBestAccuracy * 100.0f);
+ data2 = RoundFloatToInt(GetTeamAccuracy( TEAM_TERRORIST ) * 100.0f);
+ return true;
+ }
+
+ return false;
+}
+
+bool FFEVAL_BEST_COUNTERTERRORIST_ACCURACY( int &iPlayer, int &data1, int &data2, int &data3 )
+{
+ float fAccuracy = 0.0f, fBestAccuracy = 0.0f;
+ CBasePlayer *pPlayer = NULL;
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ pPlayer = UTIL_PlayerByIndex( i );
+
+ // Look only at counter-terrorist players
+ if ( pPlayer && pPlayer->GetTeamNumber() == TEAM_CT )
+ {
+ // Calculate accuracy the counter-terrorist
+ float shots = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_FIRED];
+ float hits = CCS_GameStats.FindPlayerStats(pPlayer).statsCurrentRound[CSSTAT_SHOTS_HIT];
+ if (shots > MIN_SHOTS_FOR_ACCURACY)
+ {
+ fAccuracy = (float)hits / shots;
+ }
+
+ // Track the most accurate counter-terrorist
+ if ( fAccuracy > fBestAccuracy )
+ {
+ fBestAccuracy = fAccuracy;
+ iPlayer = i;
+ }
+ }
+ }
+
+ if ( fBestAccuracy - GetTeamAccuracy( TEAM_CT ) >= 0.10f )
+ {
+ data1 = RoundFloatToInt(fBestAccuracy * 100.0f);
+ data2 = RoundFloatToInt(GetTeamAccuracy( TEAM_CT ) * 100.0f);
+ return true;
+ }
+
+ return false;
+}
+
+
+//=============================================================================
+// Fun Fact Declarations
+//=============================================================================
+
+DECLARE_FUNFACT_STATBEST( FUNFACT_DAMAGE_WITH_GRENADES, "#funfact_damage_with_grenade", 0.5f, CSSTAT_GRENADE_DAMAGE, 200, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_KNIFE_KILLS, "#funfact_knife_kills", 0.5f, CSSTAT_KILLS_KNIFE, 1, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_KILLS_WITH_GRENADES, "#funfact_kills_grenades", 0.7f, CSSTAT_KILLS_HEGRENADE, 2, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_BLIND_KILLS, "#funfact_blind_kills", 0.9f, CSSTAT_KILLS_WHILE_BLINDED, 1, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_KILLED_ENEMIES, "#funfact_killed_enemies", 0.6f, CSSTAT_KILLS, 3, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_KILLS_WITH_LAST_ROUND, "#funfact_kills_with_last_round", 0.6f, CSSTAT_KILLS_WITH_LAST_ROUND, 1, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_DONATED_WEAPONS, "#funfact_donated_weapons", 0.3f, CSSTAT_WEAPONS_DONATED, 2, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_NUM_TIMES_JUMPED, "#funfact_num_times_jumped", 0.2f, CSSTAT_TOTAL_JUMPS, 10, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_FALL_DAMAGE, "#funfact_fall_damage", 0.2f, CSSTAT_FALL_DAMAGE, 50, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_POSTHUMOUS_KILLS_WITH_GRENADE, "#funfact_posthumous_kills_with_grenade", 1.0f, CSSTAT_GRENADE_POSTHUMOUSKILLS, 1, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_ITEMS_PURCHASED, "#funfact_items_purchased", 0.2f, CSSTAT_ITEMS_PURCHASED, 5, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_NUMBER_OF_OVERKILLS, "#funfact_number_of_overkills", 0.5f, CSSTAT_DOMINATION_OVERKILLS, 2, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_MONEY_SPENT, "#funfact_money_spent", 0.2f, CSSTAT_MONEY_SPENT, 5000, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_GRENADES_THROWN, "#funfact_grenades_thrown", 0.3f, CSSTAT_GRENADES_THROWN, 2, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_DEFENDED_BOMB, "#funfact_defended_bomb", 0.5f, CSSTAT_KILLS_WHILE_DEFENDING_BOMB, 2, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_ITEMS_DROPPED_VALUE, "#funfact_items_dropped_value", 0.5f, CSTAT_ITEMS_DROPPED_VALUE, 10000, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_KILL_WOUNDED_ENEMIES, "#funfact_kill_wounded_enemies", 0.4f, CSSTAT_KILLS_ENEMY_WOUNDED, 3, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_KILLS_HEADSHOTS, "#funfact_kills_headshots", 0.7f, CSSTAT_KILLS_HEADSHOT, 3, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_BROKE_WINDOWS, "#funfact_broke_windows", 0.3f, CSSTAT_NUM_BROKEN_WINDOWS, 5, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_STATBEST( FUNFACT_NIGHTVISION_DAMAGE, "#funfact_nightvision_damage", 0.5f, CSSTAT_NIGHTVISION_DAMAGE, 100, EvalFlags::HighestOnly);
+
+DECLARE_FUNFACT_STATSUM( FUNFACT_SHOTS_FIRED, "#funfact_shots_fired", 0.1f, CSSTAT_SHOTS_FIRED, 200, EvalFlags::All);
+
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_KILL_DEFUSER, "#funfact_kill_defuser", 0.6f, FFEVAL_KILLED_DEFUSER, 1, EvalFlags::TeamTerrorist | EvalFlags::WinningTeam);
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_KILL_RESCUER, "#funfact_kill_rescuer", 0.6f, FFEVAL_KILLED_RESCUER, 1, EvalFlags::TeamTerrorist);
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_KILLS_WITH_SINGLE_GRENADE, "#funfact_kills_with_single_grenade", 0.8f, FFEVAL_KILLS_WITH_GRENADE, 2, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_DAMAGE_NO_KILLS, "#funfact_damage_no_kills", 0.4f, FFEVAL_DAMAGE_NO_KILLS, 200, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_FIRST_KILL, "#funfact_first_kill", 0.2f, FFEVAL_FIRST_KILL, 1, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_FIRST_BLOOD, "#funfact_first_blood", 0.2f, FFEVAL_FIRST_BLOOD, 1, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_BEST_ACCURACY, "#funfact_best_accuracy", 0.4f, FFEVAL_ACCURACY, 20, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_KNIFE_IN_GUNFIGHT, "#funfact_knife_in_gunfight", 0.6f, FFEVAL_KNIFE_IN_GUNFIGHT , 1, EvalFlags::All);
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_SURVIVED_MULTIPLE_ATTACKERS, "#funfact_survived_multiple_attackers", 0.3f, FFEVAL_MULTIPLE_ATTACKER_COUNT, 3, EvalFlags::HighestOnly | EvalFlags::Alive);
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_DIED_FROM_MULTIPLE_ATTACKERS, "#funfact_died_from_multiple_attackers", 0.5f, FFEVAL_MULTIPLE_ATTACKER_COUNT, 3, EvalFlags::HighestOnly | EvalFlags::Dead);
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_USED_ALL_AMMO, "#funfact_used_all_ammo", 0.5f, FFEVAL_USED_ALL_AMMO, 1, EvalFlags::All );
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_DAMAGE_MULTIPLE_ENEMIES, "#funfact_damage_multiple_enemies", 0.5f, FFEVAL_DAMAGE_MULTIPLE_ENEMIES, 3, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_USED_MULTIPLE_WEAPONS, "#funfact_used_multiple_weapons", 0.5f, FFEVAL_USED_MULTIPLE_WEAPONS, 4, EvalFlags::HighestOnly);
+DECLARE_FUNFACT_PLAYERFUNC( FUNFACT_DEFUSED_WITH_DROPPED_KIT, "#funfact_defused_with_dropped_kit", 0.4f, FFEVAL_DEFUSED_WITH_DROPPED_KIT, 1, EvalFlags::TeamCT);
+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);
+
+DECLARE_FUNFACT_EVALFUNC( FUNFACT_CT_WIN_NO_KILLS, "#funfact_ct_win_no_kills", 0.4f, FFEVAL_CT_WIN_NO_KILLS);
+DECLARE_FUNFACT_EVALFUNC( FUNFACT_T_WIN_NO_KILLS, "#funfact_t_win_no_kills", 0.4f, FFEVAL_T_WIN_NO_KILLS );
+DECLARE_FUNFACT_EVALFUNC( FUNFACT_T_WIN_NO_CASUALTIES, "#funfact_t_win_no_casualties", 0.2f, FFEVAL_T_WIN_NO_CASUALTIES );
+DECLARE_FUNFACT_EVALFUNC( FUNFACT_CT_WIN_NO_CASUALTIES, "#funfact_ct_win_no_casualties", 0.2f, FFEVAL_CT_WIN_NO_CASUALTIES );
+DECLARE_FUNFACT_EVALFUNC( FUNFACT_SHORT_ROUND, "#funfact_short_round", 0.3f, FFEVAL_SHORT_ROUND );
+DECLARE_FUNFACT_EVALFUNC( FUNFACT_WON_AS_LAST_MEMBER, "#funfact_won_as_last_member", 0.6f, FFEVAL_WON_AS_LAST_MEMBER );
+DECLARE_FUNFACT_EVALFUNC( FUNFACT_TERRORIST_ACCURACY, "#funfact_terrorist_accuracy", 0.2f, FFEVAL_TERRORIST_ACCURACY);
+DECLARE_FUNFACT_EVALFUNC( FUNFACT_CT_ACCURACY, "#funfact_ct_accuracy", 0.2f, FFEVAL_CT_ACCURACY);
+DECLARE_FUNFACT_EVALFUNC( FUNFACT_BEST_TERRORIST_ACCURACY, "#funfact_best_terrorist_accuracy", 0.3f, FFEVAL_BEST_TERRORIST_ACCURACY);
+DECLARE_FUNFACT_EVALFUNC( FUNFACT_BEST_COUNTERTERRORIST_ACCURACY, "#funfact_best_counterterrorist_accuracy", 0.3f, FFEVAL_BEST_COUNTERTERRORIST_ACCURACY);
+DECLARE_FUNFACT_EVALFUNC( FUNFACT_FALLBACK1, "#funfact_fallback1", 0.0f, FFEVAL_ALWAYS_TRUE);
+DECLARE_FUNFACT_EVALFUNC( FUNFACT_FALLBACK2, "#funfact_fallback2", 0.0f, FFEVAL_ALWAYS_TRUE);
+
+DECLARE_FUNFACT_TEAMFUNC( FUNFACT_SAME_UNIFORM_TERRORIST, "#funfact_same_uniform_terrorist", 0.5f, FFEVAL_SAME_UNIFORM, TEAM_TERRORIST);
+DECLARE_FUNFACT_TEAMFUNC( FUNFACT_SAME_UNIFORM_CT, "#funfact_same_uniform_ct", 0.5f, FFEVAL_SAME_UNIFORM, TEAM_CT);