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
793 lines
29 KiB
//========= 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);
|
|
|