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
1236 lines
45 KiB
#include "cbase.h"
|
|
#include "cs_gamerules.h"
|
|
#include "cs_gamestats.h"
|
|
#include "funfactmgr_cs.h"
|
|
#include "funfact_cs.h"
|
|
#include "weapon_csbase.h"
|
|
#include "cs_achievement_constants.h"
|
|
|
|
const float kFirstBloodTimeClassic = 30.0f;
|
|
const float kFirstKillTimeClassic = 45.0f;
|
|
const float kFirstBloodTimeDemolition = 15.0f;
|
|
const float kFirstKillTimeDemolition = 25.0f;
|
|
const float kFastRoundTimeClassic = 40.0f;
|
|
const float kFastRoundTimeElimination = 30.0f; // used for demolition fast win fun facts
|
|
const int kMinShotsForAccuracy = 15; // number of shots required for accuracy to be considered valid
|
|
const float kAccuracyAdvantage = 0.25f; // delta required for player > team
|
|
const float kMinTeamAccuracy = 0.30f; // accuracy required for team fun facts
|
|
|
|
enum FunFactId
|
|
{
|
|
FF_CT_WIN_NO_KILLS,
|
|
FF_T_WIN_NO_KILLS,
|
|
FF_KILL_DEFUSER,
|
|
FF_KILL_RESCUER,
|
|
FF_T_WIN_NO_CASUALTIES,
|
|
FF_CT_WIN_NO_CASUALTIES,
|
|
FF_DAMAGE_WITH_GRENADES,
|
|
FF_KILLS_WITH_GRENADES,
|
|
FF_SINGLE_GRENADE_KILLS,
|
|
FF_DAMAGE_NO_KILLS,
|
|
FF_KILLED_ENEMIES,
|
|
FF_FIRST_KILL,
|
|
FF_FIRST_BLOOD,
|
|
FF_SHORT_ROUND,
|
|
FF_BEST_ACCURACY,
|
|
FF_KNIFE_KILLS,
|
|
FF_BLIND_KILLS,
|
|
FF_KILLS_WITH_LAST_ROUND,
|
|
FF_DONATED_WEAPONS,
|
|
FF_POSTHUMOUS_GRENADE_KILLS,
|
|
FF_KNIFE_IN_GUNFIGHT,
|
|
FF_NUM_TIMES_JUMPED,
|
|
FF_FALL_DAMAGE,
|
|
FF_ITEMS_PURCHASED,
|
|
FF_WON_AS_LAST_MEMBER,
|
|
FF_NUMBER_OF_OVERKILLS,
|
|
FF_SHOTS_FIRED,
|
|
FF_SHOTS_FIRED_GG,
|
|
FF_MONEY_SPENT,
|
|
FF_MULTIPLE_ATTACKS_LIVED,
|
|
FF_MULTIPLE_ATTACKS_DIED,
|
|
FF_DAMAGE_MULTIPLE_ENEMIES,
|
|
FF_GRENADES_THROWN,
|
|
FF_USED_ALL_AMMO,
|
|
FF_DEFENDED_BOMB,
|
|
FF_ITEMS_DROPPED_VALUE,
|
|
FF_KILL_WOUNDED_ENEMIES,
|
|
FF_USED_MULTIPLE_WEAPONS,
|
|
FF_T_ACCURACY,
|
|
FF_CT_ACCURACY,
|
|
FF_BEST_T_ACCURACY,
|
|
FF_BEST_CT_ACCURACY,
|
|
FF_KILLS_HEADSHOTS,
|
|
FF_KILLS_WITH_STATTRAK_WEAPON,
|
|
FF_BROKE_WINDOWS,
|
|
FF_NIGHTVISION_DAMAGE,
|
|
FF_DEFUSED_WITH_DROPPED_KIT,
|
|
FF_KILLED_HALF_OF_ENEMIES,
|
|
FF_KILLED_ALL_ENEMIES,
|
|
FF_KNIFE_LEVEL_REACHED,
|
|
FF_MAX_KILLED_BEFORE_DYING,
|
|
FF_MAX_RESPAWNS,
|
|
FF_DEFAULT_WEAPON,
|
|
FF_ROUNDS_WITHOUT_DYING,
|
|
FF_TASER_KILL,
|
|
FF_TICKING_TIME,
|
|
FF_CT_WIN_TIME,
|
|
FF_TER_WIN_TIME,
|
|
FF_BOTS_ASSUMED,
|
|
FF_DOMINATION,
|
|
FF_REVENGE,
|
|
FF_STEPS_TAKEN,
|
|
FF_QUARTER_HEALTH,
|
|
FF_EMPTY_GUNS,
|
|
FF_SLOW_TRIGGER,
|
|
FF_PICKUP_BOMB,
|
|
FF_BOMB_CARRIERS,
|
|
FF_KNIFE_BOMB_PLANTER,
|
|
FF_BOMB_PLANTED_BEFORE_KILL,
|
|
FF_FAILED_BOMB_PLANTS,
|
|
FF_KNIFE_WITHOUT_AMMO,
|
|
FF_MOLOTOV_BURNS,
|
|
FF_SURVIVAL_TIME,
|
|
FF_PULLED_TRIGGER,
|
|
FF_DEFUSE_WAS_CLOSE_CALL_TENTHS,
|
|
FF_DEFUSE_WAS_CLOSE_CALL_HUNDREDTHS,
|
|
FF_DEFUSE_WAS_CLOSE_CALL_THOUSANDTHS,
|
|
FF_FALLBACK,
|
|
};
|
|
|
|
|
|
CFunFactHelper *CFunFactHelper::s_pFirst = NULL;
|
|
|
|
|
|
|
|
//=============================================================================
|
|
// Per-player evaluation Fun Fact
|
|
// Evaluate the function per player and generate a fun fact for each valid or
|
|
// highest valid player
|
|
//=============================================================================
|
|
|
|
namespace GameFlags
|
|
{
|
|
enum Type
|
|
{
|
|
None = 0x0000,
|
|
Classic = 0x0001,
|
|
Demolition = 0x0002,
|
|
GunGame = 0x0004,
|
|
AllModes = 0x0007,
|
|
NotGunGame = AllModes & ~GunGame,
|
|
Elimination = 0x0100, // win by elimination
|
|
};
|
|
};
|
|
|
|
bool GameQualifies( e_RoundEndReason roundResult, int gameFlags )
|
|
{
|
|
if ( gameFlags & GameFlags::Elimination )
|
|
{
|
|
if ( roundResult != CTs_Win && roundResult != Terrorists_Win )
|
|
return false;
|
|
}
|
|
|
|
// check to see if we're enabled to run in our game mode
|
|
if ( (gameFlags & GameFlags::Classic) && CSGameRules()->IsPlayingClassic() )
|
|
return true;
|
|
if ( (gameFlags & GameFlags::Demolition) && CSGameRules()->IsPlayingGunGameTRBomb() )
|
|
return true;
|
|
if ( (gameFlags & GameFlags::GunGame) && CSGameRules()->IsPlayingGunGameProgressive() )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// 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, int gameFlags ) :
|
|
FunFactEvaluator(id, szLocalizationToken, fCoolness),
|
|
m_pfnEval(pfnEval),
|
|
m_gameFlags(gameFlags)
|
|
{}
|
|
|
|
virtual bool Evaluate( e_RoundEndReason roundResult, FunFactVector& results ) const
|
|
{
|
|
if ( !GameQualifies(roundResult, m_gameFlags) )
|
|
return false;
|
|
|
|
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;
|
|
int m_gameFlags;
|
|
};
|
|
#define DECLARE_FUNFACT_EVALFUNC(funfactId, szLocalizationToken, fCoolness, pfnEval, gameFlags) \
|
|
static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
|
|
{ \
|
|
return new CFunFact_GenericEvalFunction(funfactId, szLocalizationToken, fCoolness, pfnEval, gameFlags); \
|
|
}; \
|
|
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 PlayerFlags
|
|
{
|
|
enum Type
|
|
{
|
|
All = 0x0000,
|
|
TeamCT = 0x0001,
|
|
TeamTerrorist = 0x0002,
|
|
HighestOnly = 0x0004, // when not set, generates fun facts for all valid testees
|
|
Alive = 0x0008,
|
|
Dead = 0x0010,
|
|
WinningTeam = 0x0020,
|
|
LosingTeam = 0x0040,
|
|
HasKilledThisRound = 0x0080,
|
|
AllowIfControlledBot = 0x0100, // allow if this player controlled a bot this round
|
|
|
|
};
|
|
};
|
|
|
|
|
|
bool PlayerQualifies( CBasePlayer* pPlayer, int playerFlags )
|
|
{
|
|
if ( (playerFlags & PlayerFlags::TeamCT) && pPlayer->GetTeamNumber() != TEAM_CT )
|
|
return false;
|
|
if ( (playerFlags & PlayerFlags::TeamTerrorist) && pPlayer->GetTeamNumber() != TEAM_TERRORIST )
|
|
return false;
|
|
|
|
if ( !pPlayer )
|
|
return false;
|
|
|
|
CCSPlayer* pCSPlayer = ToCSPlayer(pPlayer);
|
|
if( pCSPlayer && pCSPlayer->IsBot() && pCSPlayer->HasBeenControlledThisRound() )
|
|
return false; // bots are not allowed to be picked after being controlled
|
|
|
|
if ( !( playerFlags & PlayerFlags::AllowIfControlledBot ) && pCSPlayer && pCSPlayer->HasControlledBotThisRound() )
|
|
return false;
|
|
|
|
if( pPlayer->GetTeamNumber() == TEAM_SPECTATOR )
|
|
return false;
|
|
if ( (playerFlags & PlayerFlags::Dead) && const_cast<CBasePlayer*>(pPlayer)->IsAlive() ) // IsAlive() really isn't const correct
|
|
return false;
|
|
if ( (playerFlags & PlayerFlags::Alive) && !const_cast<CBasePlayer*>(pPlayer)->IsAlive() )
|
|
return false;
|
|
if ( (playerFlags & PlayerFlags::WinningTeam) && pPlayer->GetTeamNumber() != CSGameRules()->m_iRoundWinStatus )
|
|
return false;
|
|
if ( (playerFlags & PlayerFlags::LosingTeam) && pPlayer->GetTeamNumber() == CSGameRules()->m_iRoundWinStatus )
|
|
return false;
|
|
if ( ( playerFlags & PlayerFlags::HasKilledThisRound ) && CCS_GameStats.FindPlayerStats( pPlayer ).statsCurrentRound[CSSTAT_KILLS] == 0 )
|
|
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 gameFlags, int playerFlags ) :
|
|
FunFactEvaluator(id, szLocalizationToken, fCoolness),
|
|
m_pfnEval(pfnEval),
|
|
m_min(iMin),
|
|
m_gameFlags(gameFlags),
|
|
m_playerFlags(playerFlags)
|
|
{}
|
|
|
|
virtual bool Evaluate( e_RoundEndReason roundResult, FunFactVector& results ) const
|
|
{
|
|
if ( !GameQualifies(roundResult, m_gameFlags) )
|
|
return false;
|
|
|
|
int iBestValue = 0;
|
|
int iBestPlayer = 0;
|
|
bool bResult = false;
|
|
bool bTied = false;
|
|
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
CCSPlayer* pPlayer = ToCSPlayer(UTIL_PlayerByIndex( i ) );
|
|
if ( pPlayer )
|
|
{
|
|
if (!PlayerQualifies(pPlayer, m_playerFlags))
|
|
continue;
|
|
|
|
int iValue = m_pfnEval(pPlayer);
|
|
|
|
if (m_playerFlags & PlayerFlags::HighestOnly)
|
|
{
|
|
if ( iValue > iBestValue )
|
|
{
|
|
iBestValue = iValue;
|
|
iBestPlayer = i;
|
|
bTied = false;
|
|
}
|
|
else if ( iValue == iBestValue )
|
|
{
|
|
bTied = true;
|
|
}
|
|
}
|
|
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_playerFlags & PlayerFlags::HighestOnly) && iBestValue >= m_min && !bTied )
|
|
{
|
|
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_gameFlags;
|
|
int m_playerFlags;
|
|
};
|
|
#define DECLARE_FUNFACT_PLAYERFUNC(funfactId, szLocalizationToken, fCoolness, pfnEval, iMin, gameFlags, playerFlags ) \
|
|
static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
|
|
{ \
|
|
return new CFunFact_PlayerEvalFunction(funfactId, szLocalizationToken, fCoolness, pfnEval, iMin, gameFlags, playerFlags); \
|
|
}; \
|
|
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, int gameFlags ) :
|
|
FunFactEvaluator(id, szLocalizationToken, fCoolness),
|
|
m_pfnEval(pfnEval),
|
|
m_team(iTeam),
|
|
m_gameFlags(gameFlags)
|
|
{}
|
|
|
|
virtual bool Evaluate( e_RoundEndReason roundResult, FunFactVector& results ) const
|
|
{
|
|
if ( !GameQualifies(roundResult, m_gameFlags) )
|
|
return false;
|
|
|
|
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;
|
|
int m_gameFlags;
|
|
};
|
|
#define DECLARE_FUNFACT_TEAMFUNC(funfactId, szLocalizationToken, fCoolness, pfnEval, iTeam, gameFlags) \
|
|
static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
|
|
{ \
|
|
return new CFunFact_TeamEvalFunction(funfactId, szLocalizationToken, fCoolness, pfnEval, iTeam, gameFlags); \
|
|
}; \
|
|
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 gameFlags, int playerFlags ) :
|
|
FunFactEvaluator(id, szLocalizationToken, fCoolness),
|
|
m_statId(statId),
|
|
m_min(iMin),
|
|
m_gameFlags(gameFlags),
|
|
m_playerFlags(playerFlags)
|
|
{
|
|
V_strncpy(m_singularLocalizationToken, szLocalizationToken, sizeof(m_singularLocalizationToken));
|
|
if (m_min == 1)
|
|
{
|
|
V_strncat(m_singularLocalizationToken, "_singular", sizeof(m_singularLocalizationToken));
|
|
}
|
|
}
|
|
|
|
virtual bool Evaluate( e_RoundEndReason roundResult, FunFactVector& results ) const
|
|
{
|
|
if ( !GameQualifies(roundResult, m_gameFlags) )
|
|
return false;
|
|
|
|
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_playerFlags) )
|
|
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_gameFlags;
|
|
int m_playerFlags;
|
|
|
|
};
|
|
#define DECLARE_FUNFACT_STATBEST(funfactId, szLocalizationToken, fCoolness, statId, iMin, gameFlags, playerFlags) \
|
|
static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
|
|
{ \
|
|
return new CFunFact_StatBest(funfactId, szLocalizationToken, fCoolness, statId, iMin, gameFlags, playerFlags); \
|
|
}; \
|
|
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, int gameFlags, int playerFlags ) :
|
|
FunFactEvaluator(id, szLocalizationToken, fCoolness),
|
|
m_statId(statId),
|
|
m_min(iMin),
|
|
m_gameFlags(gameFlags),
|
|
m_playerFlags(playerFlags)
|
|
{}
|
|
|
|
virtual bool Evaluate( e_RoundEndReason roundResult, FunFactVector& results ) const
|
|
{
|
|
if ( !GameQualifies(roundResult, m_gameFlags) )
|
|
return false;
|
|
|
|
int iSum = 0;
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
|
|
if ( pPlayer )
|
|
{
|
|
if (!PlayerQualifies(pPlayer, m_playerFlags))
|
|
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_gameFlags;
|
|
int m_playerFlags;
|
|
};
|
|
#define DECLARE_FUNFACT_STATSUM(funfactId, szLocalizationToken, fCoolness, statId, iMin, gameFlags, playerFlags) \
|
|
static FunFactEvaluator *CreateFunFact_##funfactId( void ) \
|
|
{ \
|
|
return new CFunFact_StatSum(funfactId, szLocalizationToken, fCoolness, statId, iMin, gameFlags, playerFlags); \
|
|
}; \
|
|
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 > kMinShotsForAccuracy)
|
|
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_SINGLE_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;
|
|
}
|
|
|
|
bool FFEVAL_FIRST_KILL( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
const int timeRequired = CSGameRules()->IsPlayingClassic() ? kFirstKillTimeClassic : kFirstKillTimeDemolition;
|
|
if ( CSGameRules()->m_pFirstKill != NULL && CSGameRules()->m_firstKillTime <= timeRequired )
|
|
{
|
|
iPlayer = CSGameRules()->m_pFirstKill->entindex();
|
|
data1 = CSGameRules()->m_firstKillTime;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FFEVAL_FIRST_BLOOD( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
const int timeRequired = CSGameRules()->IsPlayingClassic() ? kFirstBloodTimeClassic : kFirstBloodTimeDemolition;
|
|
if ( CSGameRules()->m_pFirstBlood != NULL && CSGameRules()->m_firstBloodTime < timeRequired )
|
|
{
|
|
iPlayer = CSGameRules()->m_pFirstBlood->entindex();
|
|
data1 = CSGameRules()->m_firstKillTime;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FFEVAL_CT_WIN_TIME( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
if ( CSGameRules()->GetRoundElapsedTime() < kFastRoundTimeElimination && CSGameRules()->m_iRoundWinStatus == WINNER_CT )
|
|
{
|
|
data1 = CSGameRules()->GetRoundElapsedTime();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool FFEVAL_TER_WIN_TIME( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
if ( CSGameRules()->GetRoundElapsedTime() < kFastRoundTimeElimination && CSGameRules()->m_iRoundWinStatus == WINNER_TER )
|
|
{
|
|
data1 = CSGameRules()->GetRoundElapsedTime();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FFEVAL_SHORT_ROUND( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
if ( CSGameRules()->GetRoundElapsedTime() < kFastRoundTimeClassic )
|
|
{
|
|
data1 = CSGameRules()->GetRoundElapsedTime();
|
|
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 >= kMinShotsForAccuracy)
|
|
return RoundFloatToInt(100.0f * hits / shots);
|
|
return 0;
|
|
}
|
|
|
|
int FFEVAL_EMPTY_GUNS( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->GetNumFirearmsRanOutOfAmmo();
|
|
}
|
|
|
|
int FFEVAL_QUARTER_HEALTH( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->GetMediumHealthKills();
|
|
}
|
|
|
|
int FFEVAL_STEPS_TAKEN( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->GetNumFootsteps();
|
|
}
|
|
|
|
int FFEVAL_MOST_CONCURRENT_DOMINATIONS( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->GetNumConcurrentDominations();
|
|
}
|
|
|
|
|
|
int FFEVAL_MOST_BOTS_ASSUMED( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->GetNumBotsControlled();
|
|
}
|
|
|
|
|
|
int FFEVAL_HIGHEST_PROXIMITY_SCORE( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->GetRoundProximityScore();
|
|
}
|
|
|
|
int FFEVAL_ROUNDS_WITHOUT_DYING( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->GetCurNumRoundsSurvived();
|
|
}
|
|
|
|
int FFEVAL_DEFAULT_WEAPON( CCSPlayer* pPlayer )
|
|
{
|
|
if ( CSGameRules()->IsPistolRound() )
|
|
return 0;
|
|
|
|
return pPlayer->GetPickedUpWeaponThisRound() ? 0 : 1;
|
|
}
|
|
|
|
int FFEVAL_KILLED_PERCENT_OF_ENEMIES( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->GetPercentageOfEnemyTeamKilled();
|
|
}
|
|
|
|
int FFEVAL_MAX_NUM_RESPAWNS( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->m_iNumSpawns;
|
|
}
|
|
|
|
int FFEVAL_KILL_STREAK_BEFORE_DYING( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->m_maxNumEnemiesKillStreak;
|
|
}
|
|
|
|
void GetTeamRoundScore( int teamNumber, int* num_players,int* contribution_sum )
|
|
{
|
|
CBasePlayer *basePlayer = NULL;
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
basePlayer = UTIL_PlayerByIndex( i );
|
|
CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
|
|
|
|
if( pPlayer && pPlayer->GetTeamNumber() == teamNumber )
|
|
{
|
|
(*num_players)++;
|
|
(*contribution_sum) += pPlayer->GetRoundContributionScore();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FFEVAL_FAILED_BOMB_PLANTS( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
data1 = 0;
|
|
|
|
CBasePlayer *basePlayer = NULL;
|
|
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
basePlayer = UTIL_PlayerByIndex( i );
|
|
CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
|
|
|
|
if ( pPlayer &&
|
|
!(pPlayer->GetBombPlacedTime() >= 0.0f) &&
|
|
pPlayer->HasAttemptedBombPlace() )
|
|
{
|
|
iPlayer = i;
|
|
data1++;
|
|
}
|
|
}
|
|
|
|
if ( data1 >= 2 )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FFEVAL_BOMB_PLANTED_BEFORE_KILL( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
float fBombPlantedTime = -1.0f;
|
|
float fFirstKillTime = -1.0f;
|
|
CBasePlayer *basePlayer = NULL;
|
|
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
basePlayer = UTIL_PlayerByIndex( i );
|
|
CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
|
|
|
|
if ( pPlayer &&
|
|
pPlayer->GetBombPlacedTime() >= 0.0f &&
|
|
( pPlayer->GetBombPlacedTime() <= fBombPlantedTime || fBombPlantedTime < 0.0f ) )
|
|
{
|
|
iPlayer = i;
|
|
fBombPlantedTime = pPlayer->GetBombPlacedTime();
|
|
data1 = (int) fBombPlantedTime;
|
|
}
|
|
|
|
if (pPlayer &&
|
|
pPlayer->GetKilledTime() > 0.0f &&
|
|
(fFirstKillTime < 0.0f || pPlayer->GetKilledTime() < fFirstKillTime ) )
|
|
{
|
|
fFirstKillTime = pPlayer->GetKilledTime();
|
|
data2 = (int) fFirstKillTime;
|
|
}
|
|
}
|
|
|
|
|
|
if( fBombPlantedTime > 0.0f &&
|
|
(fFirstKillTime <= 0.0f || fFirstKillTime > fBombPlantedTime ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FFEVAL_PICKUP_BOMB( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
// count number of picker-upers as data1
|
|
data1 = 0;
|
|
CBasePlayer *basePlayer = NULL;
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
basePlayer = UTIL_PlayerByIndex( i );
|
|
CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
|
|
|
|
if( pPlayer && pPlayer->GetBombPickuptime() >= 0.0f )
|
|
data1++;
|
|
}
|
|
|
|
if( data1 > 2 ) // enough guys picked up the bomb to be interesting
|
|
{
|
|
// find someone who placed a bomb
|
|
CBasePlayer *basePlayer = NULL;
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
basePlayer = UTIL_PlayerByIndex( i );
|
|
CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
|
|
|
|
if( pPlayer && pPlayer->GetBombPlacedTime() >= 0.0f )
|
|
{
|
|
data1--;
|
|
iPlayer = i;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FFEVAL_TICKING_TIME( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
int winningTeam = CSGameRules()->m_iRoundWinStatus;
|
|
if ( winningTeam != TEAM_TERRORIST )
|
|
return false;
|
|
|
|
data1 = 0;
|
|
CBasePlayer *basePlayer = NULL;
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
basePlayer = UTIL_PlayerByIndex( i );
|
|
CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
|
|
|
|
if( pPlayer && pPlayer->AttemptedToDefuseBomb() )
|
|
data1++;
|
|
}
|
|
|
|
if( data1 > 1 )
|
|
return true; // we need at least two of these guys to be interesting
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool FFEVAL_KNIFE_LEVEL_REACHED( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
CBasePlayer *basePlayer = NULL;
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
basePlayer = UTIL_PlayerByIndex( i );
|
|
CCSPlayer* pPlayer = ToCSPlayer( basePlayer );
|
|
CWeaponCSBase* attackerWeapon = pPlayer ? pPlayer->GetActiveCSWeapon() : 0;
|
|
CSWeaponID attackerWeaponID = attackerWeapon ? attackerWeapon->GetCSWeaponID() : WEAPON_NONE;
|
|
if( attackerWeaponID == WEAPON_KNIFE )
|
|
data1++;
|
|
}
|
|
|
|
if( data1 > 3 )
|
|
return true; // we need at least three of these guys to be interesting
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
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_PULLED_TRIGGER( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->GetNumTriggerPulls();
|
|
}
|
|
int FFEVAL_SURVIVAL_TIME( CCSPlayer* pPlayer )
|
|
{
|
|
return (int) pPlayer->GetLongestSurvivalTime();
|
|
}
|
|
int FFEVAL_MOLOTOV_BURNS( CCSPlayer* pPlayer )
|
|
{
|
|
// DDK: note that this gets all burn damage. Currently only molotov's have burn damage
|
|
return pPlayer->GetNumPlayersDamagedWithFire();
|
|
}
|
|
int FFEVAL_KNIFE_WITHOUT_AMMO( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->GetKnifeKillsWhenOutOfAmmo();
|
|
}
|
|
int FFEVAL_KNIFE_BOMB_PLANTER( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->GetKnifeLevelKilledBombPlacer();
|
|
}
|
|
|
|
int FFEVAL_BOMB_CARRIERS( CCSPlayer* pPlayer )
|
|
{
|
|
return pPlayer->GetNumBombCarrierKills();
|
|
}
|
|
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 funfact_helper_defuse_close_call( int &iPlayer, int &data1, int &data2, int &data3, float flThreshold, float flFractionalMultiplier )
|
|
{
|
|
CBasePlayer *pPlayer = NULL;
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
pPlayer = UTIL_PlayerByIndex( i );
|
|
|
|
// Only CTs can defuse
|
|
if ( pPlayer && pPlayer->GetTeamNumber() == TEAM_CT )
|
|
{
|
|
CCSPlayer *pCSPlayer = (CCSPlayer*)pPlayer;
|
|
if ( pCSPlayer )
|
|
{
|
|
float flDefuseTimeRemaining = pCSPlayer->GetDefusedBombWithThisTimeRemaining();
|
|
if ( flDefuseTimeRemaining > 0 && flDefuseTimeRemaining <= flThreshold )
|
|
{
|
|
iPlayer = i;
|
|
data1 = RoundFloatToInt( flDefuseTimeRemaining * flFractionalMultiplier );
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FFEVAL_DEFUSE_WAS_CLOSE_CALL_TENTHS( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
return funfact_helper_defuse_close_call( iPlayer, data1, data2, data3, 0.5f, 10.0f );
|
|
}
|
|
|
|
bool FFEVAL_DEFUSE_WAS_CLOSE_CALL_HUNDREDTHS( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
return funfact_helper_defuse_close_call( iPlayer, data1, data2, data3, 0.1f, 100.0f );
|
|
}
|
|
|
|
bool FFEVAL_DEFUSE_WAS_CLOSE_CALL_THOUSANDTHS( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
return funfact_helper_defuse_close_call( iPlayer, data1, data2, data3, 0.01f, 1000.0f );
|
|
}
|
|
|
|
bool FFEVAL_T_ACCURACY( int &iPlayer, int &data1, int &data2, int &data3 )
|
|
{
|
|
float terroristAccuracy = GetTeamAccuracy(TEAM_TERRORIST);
|
|
float ctAccuracy = GetTeamAccuracy(TEAM_CT);
|
|
|
|
if (terroristAccuracy > kMinTeamAccuracy && 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 > kMinTeamAccuracy && ctAccuracy > terroristAccuracy)
|
|
{
|
|
data1 = RoundFloatToInt(ctAccuracy * 100.0f);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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 > kMinShotsForAccuracy)
|
|
{
|
|
fAccuracy = (float)hits / shots;
|
|
}
|
|
|
|
// Track the most accurate terrorist
|
|
if ( fAccuracy > fBestAccuracy )
|
|
{
|
|
fBestAccuracy = fAccuracy;
|
|
iPlayer = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( fBestAccuracy - GetTeamAccuracy( TEAM_TERRORIST ) >= kAccuracyAdvantage )
|
|
{
|
|
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 > kMinShotsForAccuracy)
|
|
{
|
|
fAccuracy = (float)hits / shots;
|
|
}
|
|
|
|
// Track the most accurate counter-terrorist
|
|
if ( fAccuracy > fBestAccuracy )
|
|
{
|
|
fBestAccuracy = fAccuracy;
|
|
iPlayer = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( fBestAccuracy - GetTeamAccuracy( TEAM_CT ) >= kAccuracyAdvantage )
|
|
{
|
|
data1 = RoundFloatToInt(fBestAccuracy * 100.0f);
|
|
data2 = RoundFloatToInt(GetTeamAccuracy( TEAM_CT ) * 100.0f);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// Fun Fact Declarations
|
|
//=============================================================================
|
|
|
|
DECLARE_FUNFACT_STATBEST( FF_KILL_WOUNDED_ENEMIES, "#funfact_kill_wounded_enemies", 0.2f, CSSTAT_KILLS_ENEMY_WOUNDED, 3, GameFlags::AllModes | GameFlags::Elimination, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_KILLS_HEADSHOTS, "#funfact_kills_headshots", 0.4f, CSSTAT_KILLS_HEADSHOT, 2, GameFlags::AllModes, PlayerFlags::HighestOnly );
|
|
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 );
|
|
DECLARE_FUNFACT_STATBEST( FF_NUM_TIMES_JUMPED, "#funfact_num_times_jumped", 0.1f, CSSTAT_TOTAL_JUMPS, 20, GameFlags::AllModes, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_KILLED_ENEMIES, "#funfact_killed_enemies", 0.4f, CSSTAT_KILLS, 3, GameFlags::NotGunGame | GameFlags::Elimination, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_KNIFE_KILLS, "#funfact_knife_kills", 0.4f, CSSTAT_KILLS_KNIFE, 1, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_REVENGE, "#funfact_revenge", 0.3f, CSSTAT_REVENGES, 1, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_BLIND_KILLS, "#funfact_blind_kills", 0.9f, CSSTAT_KILLS_WHILE_BLINDED, 1, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_DAMAGE_WITH_GRENADES, "#funfact_damage_with_grenade", 0.5f, CSSTAT_GRENADE_DAMAGE, 150, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_KILLS_WITH_GRENADES, "#funfact_kills_grenades", 0.7f, CSSTAT_KILLS_HEGRENADE, 2, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_POSTHUMOUS_GRENADE_KILLS,"#funfact_posthumous_kills_with_grenade", 1.0f, CSSTAT_GRENADE_POSTHUMOUSKILLS, 1, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_GRENADES_THROWN, "#funfact_grenades_thrown", 0.3f, CSSTAT_GRENADES_THROWN, 3, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_DEFENDED_BOMB, "#funfact_defended_bomb", 0.5f, CSSTAT_KILLS_WHILE_DEFENDING_BOMB, 2, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
|
|
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 );
|
|
DECLARE_FUNFACT_STATBEST( FF_DONATED_WEAPONS, "#funfact_donated_weapons", 0.3f, CSSTAT_WEAPONS_DONATED, 2, GameFlags::Classic, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_ITEMS_PURCHASED, "#funfact_items_purchased", 0.2f, CSSTAT_ITEMS_PURCHASED, 4, GameFlags::Classic, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_FALL_DAMAGE, "#funfact_fall_damage", 0.2f, CSSTAT_FALL_DAMAGE, 50, GameFlags::Classic, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_BROKE_WINDOWS, "#funfact_broke_windows", 0.1f, CSSTAT_NUM_BROKEN_WINDOWS, 5, GameFlags::Classic, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_TASER_KILL, "#funfact_taser_kill", 0.7f, CSSTAT_KILLS_TASER, 1, GameFlags::Classic, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_NUMBER_OF_OVERKILLS, "#funfact_number_of_overkills", 0.5f, CSSTAT_DOMINATION_OVERKILLS, 2, GameFlags::Classic, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_MONEY_SPENT, "#funfact_money_spent", 0.2f, CSSTAT_MONEY_SPENT, 5000, GameFlags::Classic, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_STATBEST( FF_ITEMS_DROPPED_VALUE, "#funfact_items_dropped_value", 0.4f, CSTAT_ITEMS_DROPPED_VALUE, 10000, GameFlags::Classic, PlayerFlags::HighestOnly );
|
|
|
|
DECLARE_FUNFACT_STATSUM( FF_SHOTS_FIRED, "#funfact_shots_fired", 0.2f, CSSTAT_SHOTS_FIRED, 100, GameFlags::NotGunGame, PlayerFlags::All );
|
|
DECLARE_FUNFACT_STATSUM( FF_SHOTS_FIRED_GG, "#funfact_shots_fired", 0.1f, CSSTAT_SHOTS_FIRED, 250, GameFlags::GunGame, PlayerFlags::All );
|
|
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_BEST_ACCURACY, "#funfact_best_accuracy", 0.2f, FFEVAL_ACCURACY, 50, GameFlags::AllModes, PlayerFlags::HighestOnly | PlayerFlags::Alive );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_BOTS_ASSUMED, "#funfact_bots_assumed", 0.2f, FFEVAL_MOST_BOTS_ASSUMED, 2, GameFlags::AllModes, PlayerFlags::HighestOnly | PlayerFlags::AllowIfControlledBot );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_DOMINATION, "#funfact_domination", 0.3f, FFEVAL_MOST_CONCURRENT_DOMINATIONS, 1, GameFlags::AllModes, PlayerFlags::HighestOnly | PlayerFlags::HasKilledThisRound );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_USED_ALL_AMMO, "#funfact_used_all_ammo", 0.5f, FFEVAL_USED_ALL_AMMO, 1, GameFlags::NotGunGame, PlayerFlags::All );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_DAMAGE_NO_KILLS, "#funfact_damage_no_kills", 0.2f, FFEVAL_DAMAGE_NO_KILLS, 250, GameFlags::NotGunGame, PlayerFlags::All );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_SINGLE_GRENADE_KILLS, "#funfact_kills_with_single_grenade", 0.8f, FFEVAL_KILLS_SINGLE_GRENADE, 2, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_EMPTY_GUNS, "#funfact_empty_guns", 0.3f, FFEVAL_EMPTY_GUNS, 2, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_MULTIPLE_ATTACKS_LIVED, "#funfact_survived_multiple_attackers", 0.4f, FFEVAL_MULTIPLE_ATTACKER_COUNT, 3, GameFlags::NotGunGame, PlayerFlags::HighestOnly | PlayerFlags::Alive );
|
|
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 );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_DAMAGE_MULTIPLE_ENEMIES, "#funfact_damage_multiple_enemies", 0.2f, FFEVAL_DAMAGE_MULTIPLE_ENEMIES, 3, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_BOMB_CARRIERS, "#funfact_bomb_carriers", 0.3f, FFEVAL_BOMB_CARRIERS, 2, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_KNIFE_BOMB_PLANTER, "#funfact_knife_bomb_planter", 0.8f, FFEVAL_KNIFE_BOMB_PLANTER, 1, GameFlags::NotGunGame, PlayerFlags::HighestOnly | PlayerFlags::Alive );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_KNIFE_WITHOUT_AMMO, "#funfact_knife_without_ammo", 0.3f, FFEVAL_KNIFE_WITHOUT_AMMO, 2, GameFlags::NotGunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_KILL_DEFUSER, "#funfact_kill_defuser", 0.5f, FFEVAL_KILLED_DEFUSER, 1, GameFlags::NotGunGame, PlayerFlags::TeamTerrorist | PlayerFlags::WinningTeam );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_KILL_RESCUER, "#funfact_kill_rescuer", 0.5f, FFEVAL_KILLED_RESCUER, 1, GameFlags::Classic, PlayerFlags::TeamTerrorist );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_DEFAULT_WEAPON, "#funfact_default_weapon", 0.1f, FFEVAL_DEFAULT_WEAPON, 1, GameFlags::Classic, PlayerFlags::WinningTeam | PlayerFlags::HasKilledThisRound | PlayerFlags::Alive );
|
|
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 );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_KNIFE_IN_GUNFIGHT, "#funfact_knife_in_gunfight", 0.4f, FFEVAL_KNIFE_IN_GUNFIGHT , 1, GameFlags::Classic, PlayerFlags::All );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_USED_MULTIPLE_WEAPONS, "#funfact_used_multiple_weapons", 0.5f, FFEVAL_USED_MULTIPLE_WEAPONS, 4, GameFlags::Classic, PlayerFlags::HighestOnly );
|
|
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 );
|
|
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 );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_KILLED_ALL_ENEMIES, "#funfact_ace", 0.9f, FFEVAL_KILLED_PERCENT_OF_ENEMIES, 100, GameFlags::Classic, PlayerFlags::WinningTeam | PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_MOLOTOV_BURNS, "#funfact_molotov_burns", 0.3f, FFEVAL_MOLOTOV_BURNS, 2, GameFlags::Classic, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_MAX_KILLED_BEFORE_DYING, "#funfact_killed_before_dying", 0.3f, FFEVAL_KILL_STREAK_BEFORE_DYING, 3, GameFlags::GunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_MAX_RESPAWNS, "#funfact_respawned", 0.3f, FFEVAL_MAX_NUM_RESPAWNS, 3, GameFlags::GunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_SURVIVAL_TIME, "#funfact_survival_time", 0.2f, FFEVAL_SURVIVAL_TIME, 20, GameFlags::GunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_PULLED_TRIGGER, "#funfact_pulled_trigger", 0.1f, FFEVAL_PULLED_TRIGGER, 20, GameFlags::GunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_STEPS_TAKEN, "#funfact_steps_taken", 0.1f, FFEVAL_STEPS_TAKEN, 100, GameFlags::GunGame, PlayerFlags::HighestOnly );
|
|
DECLARE_FUNFACT_PLAYERFUNC( FF_QUARTER_HEALTH, "#funfact_quarter_health", 0.3f, FFEVAL_QUARTER_HEALTH, 2, GameFlags::GunGame, PlayerFlags::HighestOnly );
|
|
|
|
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 );
|
|
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 );
|
|
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 );
|
|
|
|
DECLARE_FUNFACT_EVALFUNC( FF_CT_WIN_NO_KILLS, "#funfact_ct_win_no_kills", 0.4f, FFEVAL_CT_WIN_NO_KILLS, GameFlags::NotGunGame );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_T_WIN_NO_KILLS, "#funfact_t_win_no_kills", 0.4f, FFEVAL_T_WIN_NO_KILLS, GameFlags::NotGunGame );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_T_WIN_NO_CASUALTIES, "#funfact_t_win_no_casualties", 0.2f, FFEVAL_T_WIN_NO_CASUALTIES, GameFlags::NotGunGame );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_CT_WIN_NO_CASUALTIES, "#funfact_ct_win_no_casualties", 0.2f, FFEVAL_CT_WIN_NO_CASUALTIES, GameFlags::NotGunGame );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_FIRST_KILL, "#funfact_first_kill", 0.2f, FFEVAL_FIRST_KILL, GameFlags::NotGunGame );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_FIRST_BLOOD, "#funfact_first_blood", 0.2f, FFEVAL_FIRST_BLOOD, GameFlags::NotGunGame );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_WON_AS_LAST_MEMBER, "#funfact_won_as_last_member", 0.6f, FFEVAL_WON_AS_LAST_MEMBER, GameFlags::NotGunGame );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_SHORT_ROUND, "#funfact_short_round", 0.2f, FFEVAL_SHORT_ROUND, GameFlags::Classic );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_T_ACCURACY, "#funfact_terrorist_accuracy", 0.1f, FFEVAL_T_ACCURACY, GameFlags::AllModes );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_CT_ACCURACY, "#funfact_ct_accuracy", 0.1f, FFEVAL_CT_ACCURACY, GameFlags::AllModes );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_BEST_T_ACCURACY, "#funfact_best_terrorist_accuracy", 0.1f, FFEVAL_BEST_TERRORIST_ACCURACY, GameFlags::AllModes );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_BEST_CT_ACCURACY, "#funfact_best_counterterrorist_accuracy", 0.1f, FFEVAL_BEST_COUNTERTERRORIST_ACCURACY, GameFlags::AllModes );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_KNIFE_LEVEL_REACHED, "#funfact_knife_level_reached", 0.3f, FFEVAL_KNIFE_LEVEL_REACHED, GameFlags::GunGame );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_TICKING_TIME, "#funfact_ticking_time", 0.3f, FFEVAL_TICKING_TIME, GameFlags::Demolition );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_CT_WIN_TIME, "#funfact_ct_win_time", 0.2f, FFEVAL_CT_WIN_TIME , GameFlags::NotGunGame | GameFlags::Elimination );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_TER_WIN_TIME, "#funfact_ter_win_time", 0.2f, FFEVAL_TER_WIN_TIME, GameFlags::NotGunGame | GameFlags::Elimination );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_PICKUP_BOMB, "#funfact_pickup_bomb", 0.3f, FFEVAL_PICKUP_BOMB, GameFlags::Demolition );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_BOMB_PLANTED_BEFORE_KILL,"#funfact_bomb_planted_before_kill", 0.3f, FFEVAL_BOMB_PLANTED_BEFORE_KILL, GameFlags::Demolition );
|
|
DECLARE_FUNFACT_EVALFUNC( FF_FAILED_BOMB_PLANTS, "#funfact_failed_bomb_plants", 0.3f, FFEVAL_FAILED_BOMB_PLANTS, GameFlags::Demolition );
|
|
|
|
DECLARE_FUNFACT_EVALFUNC( FF_FALLBACK, "", 0.0f, FFEVAL_ALWAYS_TRUE, GameFlags::AllModes );
|