source-engine/game/shared/tf2base/achievements_tf.cpp
2022-08-13 03:20:41 +03:00

564 lines
16 KiB
C++

//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "cbase.h"
#ifdef CLIENT_DLL
#include "achievementmgr.h"
#include "baseachievement.h"
#include "tf_hud_statpanel.h"
#include "c_tf_team.h"
#include "c_tf_player.h"
CAchievementMgr g_AchievementMgrTF; // global achievement mgr for TF
bool CheckWinNoEnemyCaps( IGameEvent *event, int iRole );
// Grace period that we allow a player to start after level init and still consider them to be participating for the full round. This is fairly generous
// because it can in some cases take a client several minutes to connect with respect to when the server considers the game underway
#define TF_FULL_ROUND_GRACE_PERIOD ( 4 * 60.0f )
bool IsLocalTFPlayerClass( int iClass );
// helper class for achievements that check that the player was playing on a game team for the full round
class CTFAchievementFullRound : public CBaseAchievement
{
DECLARE_CLASS( CTFAchievementFullRound, CBaseAchievement );
public:
void Init()
{
m_iFlags |= ACH_FILTER_FULL_ROUND_ONLY;
}
virtual void ListenForEvents()
{
ListenForGameEvent( "teamplay_round_win" );
}
void FireGameEvent_Internal( IGameEvent *event )
{
if ( 0 == Q_strcmp( event->GetName(), "teamplay_round_win" ) )
{
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if ( pLocalPlayer )
{
// is the player currently on a game team?
int iTeam = pLocalPlayer->GetTeamNumber();
if ( iTeam >= FIRST_GAME_TEAM )
{
float flRoundTime = event->GetFloat( "round_time", 0 );
if ( flRoundTime > 0 )
{
Event_OnRoundComplete( flRoundTime, event );
}
}
}
}
}
virtual void Event_OnRoundComplete( float flRoundTime, IGameEvent *event ) = 0 ;
};
class CAchievementTFPlayGameEveryClass : public CTFAchievementFullRound
{
DECLARE_CLASS( CAchievementTFPlayGameEveryClass, CTFAchievementFullRound );
void Init()
{
SetFlags( ACH_SAVE_GLOBAL | ACH_HAS_COMPONENTS | ACH_FILTER_FULL_ROUND_ONLY );
SetGoal( TF_LAST_NORMAL_CLASS - TF_FIRST_NORMAL_CLASS + 1 );
BaseClass::Init();
}
virtual void Event_OnRoundComplete( float flRoundTime, IGameEvent *event )
{
float flLastClassChangeTime = m_pAchievementMgr->GetLastClassChangeTime();
if ( flLastClassChangeTime > 0 )
{
// has the player been present and not changed class since the start of this round (minus a grace period)?
if ( flLastClassChangeTime < ( gpGlobals->curtime - flRoundTime ) + TF_FULL_ROUND_GRACE_PERIOD )
{
C_TFPlayer *pTFPlayer = C_TFPlayer::GetLocalTFPlayer();
if ( pTFPlayer )
{
int iClass = pTFPlayer->GetPlayerClass()->GetClassIndex();
if ( iClass >= TF_FIRST_NORMAL_CLASS && iClass <= TF_LAST_NORMAL_CLASS )
{
// yes, the achievement is satisfied for this class, set the corresponding bit
int iBitNumber =( iClass - TF_FIRST_NORMAL_CLASS );
EnsureComponentBitSetAndEvaluate( iBitNumber );
}
}
}
}
}
};
DECLARE_ACHIEVEMENT( CAchievementTFPlayGameEveryClass, ACHIEVEMENT_TF_PLAY_GAME_EVERYCLASS, "TF_PLAY_GAME_EVERYCLASS", 5 );
class CAchievementTFPlayGameEveryMap : public CTFAchievementFullRound
{
DECLARE_CLASS( CAchievementTFPlayGameEveryMap, CTFAchievementFullRound );
void Init()
{
SetFlags( ACH_SAVE_GLOBAL | ACH_HAS_COMPONENTS | ACH_FILTER_FULL_ROUND_ONLY );
static const char *szComponents[] =
{
"cp_dustbowl", "cp_granary", "cp_gravelpit", "cp_well", "ctf_2fort", "tc_hydro"
};
m_pszComponentNames = szComponents;
m_iNumComponents = ARRAYSIZE( szComponents );
SetGoal( m_iNumComponents );
}
virtual void ListenForEvents()
{
ListenForGameEvent( "teamplay_round_win" );
}
virtual void Event_OnRoundComplete( float flRoundTime, IGameEvent *event )
{
float flTeamplayStartTime = m_pAchievementMgr->GetTeamplayStartTime();
if ( flTeamplayStartTime > 0 )
{
// has the player been present and on a game team since the start of this round (minus a grace period)?
if ( flTeamplayStartTime < ( gpGlobals->curtime - flRoundTime ) + TF_FULL_ROUND_GRACE_PERIOD )
{
// yes, the achievement is satisfied for this map, set the corresponding bit
OnComponentEvent( m_pAchievementMgr->GetMapName() );
}
}
}
};
DECLARE_ACHIEVEMENT( CAchievementTFPlayGameEveryMap, ACHIEVEMENT_TF_PLAY_GAME_EVERYMAP, "TF_PLAY_GAME_EVERYMAP", 5 );
class CAchievementTFGetHealPoints : public CBaseAchievement
{
void Init()
{
SetFlags( ACH_SAVE_GLOBAL );
SetGoal( 25000 );
}
void OnPlayerStatsUpdate()
{
ClassStats_t &classStats = CTFStatPanel::GetClassStats( TF_CLASS_MEDIC );
int iOldCount = m_iCount;
m_iCount = classStats.accumulated.m_iStat[TFSTAT_HEALING];
if ( m_iCount != iOldCount )
{
m_pAchievementMgr->SetDirty( true );
}
if ( IsLocalTFPlayerClass( TF_CLASS_MEDIC ) )
{
EvaluateNewAchievement();
}
}
};
DECLARE_ACHIEVEMENT( CAchievementTFGetHealPoints, ACHIEVEMENT_TF_GET_HEALPOINTS, "TF_GET_HEALPOINTS", 5 );
class CAchievementTFBurnPlayersInMinimumTime : public CBaseAchievement
{
void Init()
{
SetFlags( ACH_SAVE_GLOBAL );
SetGoal( 1 );
}
// server fires an event for this achievement, no other code within achievement necessary
};
DECLARE_ACHIEVEMENT( CAchievementTFBurnPlayersInMinimumTime, ACHIEVEMENT_TF_BURN_PLAYERSINMINIMIMTIME, "TF_BURN_PLAYERSINMINIMUMTIME", 5 );
class CAchievementTFGetTurretKills : public CBaseAchievement
{
void Init()
{
SetFlags( ACH_SAVE_GLOBAL );
SetGoal( 1 );
}
// server fires an event for this achievement, no other code within achievement necessary
};
DECLARE_ACHIEVEMENT( CAchievementTFGetTurretKills, ACHIEVEMENT_TF_GET_TURRETKILLS, "TF_GET_TURRETKILLS", 5 );
class CAchievementTFGetHeadshots: public CBaseAchievement
{
void Init()
{
SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL );
SetGoal( 25 );
}
virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event )
{
// was this a headshot by this player?
if ( ( pAttacker == C_TFPlayer::GetLocalTFPlayer() ) && ( event->GetInt( "customkill" ) == TF_DMG_CUSTOM_HEADSHOT ) )
{
// Increment count. Count will also get slammed whenever we get a stats update from server. They should generally agree,
// but server is authoritative.
IncrementCount();
}
}
void OnPlayerStatsUpdate()
{
// when stats are updated by server, use most recent stat value
ClassStats_t &classStats = CTFStatPanel::GetClassStats( TF_CLASS_SNIPER );
int iOldCount = m_iCount;
m_iCount = classStats.accumulated.m_iStat[TFSTAT_HEADSHOTS];
if ( m_iCount != iOldCount )
{
m_pAchievementMgr->SetDirty( true );
}
if ( IsLocalTFPlayerClass( TF_CLASS_SNIPER ) )
{
EvaluateNewAchievement();
}
}
};
DECLARE_ACHIEVEMENT( CAchievementTFGetHeadshots, ACHIEVEMENT_TF_GET_HEADSHOTS, "TF_GET_HEADSHOTS", 5 );
class CAchievementTFKillNemesis : public CBaseAchievement
{
void Init()
{
SetFlags( ACH_LISTEN_KILL_EVENTS | ACH_SAVE_GLOBAL );
SetGoal( 5 );
}
virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event )
{
if ( ( ( event->GetInt( "revenge" ) > 0 ) ) && ( pAttacker == C_TFPlayer::GetLocalTFPlayer() ) )
{
// local player got revenge as primary killer
IncrementCount();
}
else if ( event->GetInt( "assister_revenge" ) > 0 )
{
int iAssisterIndex = engine->GetPlayerForUserID( event->GetInt( "assister" ) );
if ( iAssisterIndex > 0 )
{
CBaseEntity *pAssister = UTIL_PlayerByIndex( iAssisterIndex );
if ( pAssister && ( pAssister == C_TFPlayer::GetLocalTFPlayer() ) )
{
// local player got revenge as assister
IncrementCount();
}
}
}
}
};
DECLARE_ACHIEVEMENT( CAchievementTFKillNemesis, ACHIEVEMENT_TF_KILL_NEMESIS, "TF_KILL_NEMESIS", 5 );
class CAchievementTFGetConsecutiveKillsNoDeaths : public CBaseAchievement
{
void Init()
{
SetFlags( ACH_LISTEN_KILL_EVENTS | ACH_SAVE_GLOBAL );
SetGoal( 1 );
m_iConsecutiveKills = 0;
}
virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event )
{
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if ( pLocalPlayer == pVictim )
{
m_iConsecutiveKills = 0;
}
else if ( pLocalPlayer == pAttacker )
{
m_iConsecutiveKills++;
if ( 5 == m_iConsecutiveKills )
{
IncrementCount();
}
}
}
int m_iConsecutiveKills;
};
DECLARE_ACHIEVEMENT( CAchievementTFGetConsecutiveKillsNoDeaths, ACHIEVEMENT_TF_GET_CONSECUTIVEKILLS_NODEATHS, "TF_GET_CONSECUTIVEKILLS_NODEATHS", 10 );
class CAchievementTFGetHealedByEnemy: public CBaseAchievement
{
void Init()
{
SetFlags( ACH_SAVE_GLOBAL );
SetGoal( 1 );
}
// server fires an event for this achievement, no other code within achievement necessary
};
DECLARE_ACHIEVEMENT( CAchievementTFGetHealedByEnemy, ACHIEVEMENT_TF_GET_HEALED_BYENEMY, "TF_GET_HEALED_BYENEMY", 15 );
class CAchievementTFPlayGameFriendsOnly : public CBaseAchievement
{
void Init()
{
SetFlags( ACH_SAVE_GLOBAL | ACH_FILTER_FULL_ROUND_ONLY );
SetGoal( 1 );
}
virtual void ListenForEvents()
{
ListenForGameEvent( "teamplay_round_win" );
}
void FireGameEvent_Internal( IGameEvent *event )
{
if ( 0 == Q_strcmp( event->GetName(), "teamplay_round_win" ) )
{
// Are there at least 7 friends in the game? (at least 8 players total)
if ( CalcPlayersOnFriendsList( 7 ) )
{
IncrementCount();
}
}
}
};
DECLARE_ACHIEVEMENT( CAchievementTFPlayGameFriendsOnly, ACHIEVEMENT_TF_PLAY_GAME_FRIENDSONLY, "TF_PLAY_GAME_FRIENDSONLY", 10 );
class CAchievementTFWinMultipleGames : public CTFAchievementFullRound
{
DECLARE_CLASS( CAchievementTFWinMultipleGames, CTFAchievementFullRound );
void Init()
{
SetFlags( ACH_SAVE_GLOBAL | ACH_FILTER_FULL_ROUND_ONLY );
SetGoal( 20 );
BaseClass::Init();
}
virtual void Event_OnRoundComplete( float flRoundTime, IGameEvent *event )
{
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
if ( pLocalPlayer )
{
// was the player on the winning team?
int iPlayerTeam = pLocalPlayer->GetTeamNumber();
int iWinningTeam = event->GetInt( "team" );
if ( ( iWinningTeam >= FIRST_GAME_TEAM ) && ( iPlayerTeam == iWinningTeam ) )
{
IncrementCount();
}
}
}
};
DECLARE_ACHIEVEMENT( CAchievementTFWinMultipleGames, ACHIEVEMENT_TF_WIN_MULTIPLEGAMES, "TF_WIN_MULTIPLEGAMES", 10 );
class CAchievementTFGetMultipleKills : public CBaseAchievement
{
void Init()
{
// listen for player kill enemy events, base class will increment count each time that happens
SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL );
SetGoal( 1000 );
}
void OnPlayerStatsUpdate()
{
// when stats are updated by server, use most recent stat values
int iKills = 0;
// get sum of kills per class across all classes to get total kills
for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass <= TF_LAST_NORMAL_CLASS; iClass++ )
{
ClassStats_t &classStats = CTFStatPanel::GetClassStats( iClass );
iKills += classStats.accumulated.m_iStat[TFSTAT_KILLS];
}
int iOldCount = m_iCount;
m_iCount = iKills;
if ( m_iCount != iOldCount )
{
m_pAchievementMgr->SetDirty( true );
}
EvaluateNewAchievement();
}
};
DECLARE_ACHIEVEMENT( CAchievementTFGetMultipleKills, ACHIEVEMENT_TF_GET_MULTIPLEKILLS, "TF_GET_MULTIPLEKILLS", 15 );
class CAchievementTFWin2FortNoEnemyCaps : public CBaseAchievement
{
void Init()
{
SetFlags( ACH_SAVE_GLOBAL );
SetGoal( 1 );
SetMapNameFilter( "ctf_2fort" );
}
virtual void ListenForEvents()
{
ListenForGameEvent( "teamplay_round_win" );
}
void FireGameEvent_Internal( IGameEvent *event )
{
if ( 0 == Q_strcmp( event->GetName(), "teamplay_round_win" ) )
{
if ( event->GetBool( "was_sudden_death" ) == false )
{
if ( event->GetInt( "team" ) == GetLocalPlayerTeam() )
{
// did the enemy team get any flag captures?
C_TFTeam *pEnemyTeam = GetGlobalTFTeam( TF_TEAM_BLUE + TF_TEAM_RED - GetLocalPlayerTeam() );
if ( 0 == pEnemyTeam->GetFlagCaptures() )
{
IncrementCount();
}
}
}
}
}
};
DECLARE_ACHIEVEMENT( CAchievementTFWin2FortNoEnemyCaps, ACHIEVEMENT_TF_WIN_2FORT_NOENEMYCAPS, "TF_WIN_2FORT_NOENEMYCAPS", 5 );
class CAchievementTFWinWellMinimumTime : public CBaseAchievement
{
void Init()
{
SetFlags( ACH_SAVE_GLOBAL );
SetGoal( 1 );
SetMapNameFilter( "cp_well" );
}
virtual void ListenForEvents()
{
ListenForGameEvent( "teamplay_round_win" );
}
void FireGameEvent_Internal( IGameEvent *event )
{
if ( 0 == Q_strcmp( event->GetName(), "teamplay_round_win" ) )
{
if ( event->GetInt( "team" ) == GetLocalPlayerTeam() )
{
float flRoundTime = event->GetFloat( "round_time", 0 );
if ( flRoundTime > 0 && flRoundTime < 5 * 60 )
{
IncrementCount();
}
}
}
}
};
DECLARE_ACHIEVEMENT( CAchievementTFWinWellMinimumTime, ACHIEVEMENT_TF_WIN_WELL_MINIMUMTIME, "TF_WIN_WELL_MINIMUMTIME", 10 );
class CAchievementTFWinHydroNoEnemyCaps : public CBaseAchievement
{
void Init()
{
SetFlags( ACH_SAVE_GLOBAL | ACH_FILTER_FULL_ROUND_ONLY );
SetGoal( 1 );
SetMapNameFilter( "tc_hydro" );
}
virtual void ListenForEvents()
{
ListenForGameEvent( "teamplay_round_win" );
}
void FireGameEvent_Internal( IGameEvent *event )
{
// winning hydro with no enemy caps means there were 2 previous minirounds completed (3 total for a shutout)
// and local player's team won the final round
if ( ( 2 == m_pAchievementMgr->GetMiniroundsCompleted() ) && ( CheckWinNoEnemyCaps( event, TEAM_ROLE_NONE ) ) )
{
IncrementCount();
}
}
};
DECLARE_ACHIEVEMENT( CAchievementTFWinHydroNoEnemyCaps, ACHIEVEMENT_TF_WIN_HYDRO_NOENEMYCAPS, "TF_WIN_HYDRO_NOENEMYCAPS", 20 );
class CAchievementTFWinDustbowlNoEnemyCaps : public CBaseAchievement
{
void Init()
{
SetFlags( ACH_SAVE_GLOBAL | ACH_FILTER_FULL_ROUND_ONLY );
SetGoal( 1 );
SetMapNameFilter( "cp_dustbowl" );
}
virtual void ListenForEvents()
{
ListenForGameEvent( "teamplay_round_win" );
}
void FireGameEvent_Internal( IGameEvent *event )
{
// defending dustbowl with no enemy caps means there were no previous minirounds completed (that would be an attacker capture),
// the player was on the defending team and they won with no enemy caps
if ( ( 0 == m_pAchievementMgr->GetMiniroundsCompleted() ) && CheckWinNoEnemyCaps( event, TEAM_ROLE_DEFENDERS ) )
{
IncrementCount();
}
}
};
DECLARE_ACHIEVEMENT( CAchievementTFWinDustbowlNoEnemyCaps, ACHIEVEMENT_TF_WIN_DUSTBOWL_NOENEMYCAPS, "TF_WIN_DUSTBOWL_NOENEMYCAPS", 10 );
class CAchievementTFWinGravelPitNoEnemyCaps : public CBaseAchievement
{
void Init()
{
SetFlags( ACH_SAVE_GLOBAL );
SetGoal( 1 );
SetMapNameFilter( "cp_gravelpit" );
}
virtual void ListenForEvents()
{
ListenForGameEvent( "teamplay_round_win" );
}
void FireGameEvent_Internal( IGameEvent *event )
{
// Was player on defenders and won with no enemy caps?
if ( CheckWinNoEnemyCaps( event, TEAM_ROLE_DEFENDERS ) )
{
IncrementCount();
}
}
};
DECLARE_ACHIEVEMENT( CAchievementTFWinGravelPitNoEnemyCaps, ACHIEVEMENT_TF_WIN_GRAVELPIT_NOENEMYCAPS, "TF_WIN_GRAVELPIT_NOENEMYCAPS", 30 );
//-----------------------------------------------------------------------------
// Purpose: see if a round win was a win for the local player with no enemy caps
//-----------------------------------------------------------------------------
bool CheckWinNoEnemyCaps( IGameEvent *event, int iRole )
{
if ( 0 == Q_strcmp( event->GetName(), "teamplay_round_win" ) )
{
if ( event->GetInt( "team" ) == GetLocalPlayerTeam() )
{
int iLosingTeamCaps = event->GetInt( "losing_team_num_caps" );
if ( 0 == iLosingTeamCaps )
{
C_TFTeam *pLocalTeam = GetGlobalTFTeam( GetLocalPlayerTeam() );
if ( pLocalTeam )
{
int iRolePlayer = pLocalTeam->GetRole();
if ( iRole > TEAM_ROLE_NONE && ( iRolePlayer != iRole ) )
return false;
return true;
}
}
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Helper function to determine if local player is specified class
//-----------------------------------------------------------------------------
bool IsLocalTFPlayerClass( int iClass )
{
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
return( pLocalPlayer && pLocalPlayer->IsPlayerClass( iClass ) );
}
#endif // CLIENT_DLL