2024-08-29 19:18:30 -04:00
//====== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
# include "cbase.h"
# ifdef _WIN32
# include "winerror.h"
# endif
# include "achievementmgr.h"
# include "icommandline.h"
# include "KeyValues.h"
# include "filesystem.h"
# include "inputsystem/InputEnums.h"
# include "usermessages.h"
# include "fmtstr.h"
# ifdef CLIENT_DLL
# include "achievement_notification_panel.h"
# include "c_playerresource.h"
# else
# include "enginecallback.h"
# endif // CLIENT_DLL
# ifndef _X360
# include "steam/isteamuserstats.h"
# include "steam/isteamfriends.h"
# include "steam/isteamutils.h"
# else
# include "xbox/xbox_win32stubs.h"
# endif
# include "tier3/tier3.h"
# include "vgui/ILocalize.h"
# ifdef _X360
# include "GameUI/IGameUI.h"
# include "ixboxsystem.h"
# include "ienginevgui.h"
# endif // _X360
# include "matchmaking/imatchframework.h"
# include "tier0/vprof.h"
// NOTE: This has to be the last file included!
# include "tier0/memdbgon.h"
ConVar cc_achievement_debug ( " achievement_debug " , " 0 " , FCVAR_CHEAT | FCVAR_REPLICATED , " Turn on achievement debug msgs. " ) ;
const char * COM_GetModDirectory ( ) ;
# define matchmaking g_pMatchFramework->GetMatchmaking()
extern ConVar developer ;
# ifdef _X360
static CDllDemandLoader g_GameUI ( " gameui " ) ; // FIXME: This is duplicated!
# endif // _X360
# ifdef DEDICATED
// Hack this for now until we get steam_api recompiling in the Steam codebase.
ISteamUserStats * SteamUserStats ( )
{
return NULL ;
}
# endif
# if defined( XBX_GetPrimaryUserId )
# undef XBX_GetPrimaryUserId
# endif
static int AchievementIDCompare ( CBaseAchievement * const * ach1 , CBaseAchievement * const * ach2 )
{
return ( * ach1 ) - > GetAchievementID ( ) < ( * ach2 ) - > GetAchievementID ( ) ;
}
static int AchievementOrderCompare ( CBaseAchievement * const * ach1 , CBaseAchievement * const * ach2 )
{
return ( * ach1 ) - > GetDisplayOrder ( ) - ( * ach2 ) - > GetDisplayOrder ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
CAchievementMgr : : CAchievementMgr ( ) : CAutoGameSystemPerFrame ( " CAchievementMgr " )
# if !defined(NO_STEAM)
, m_CallbackUserStatsReceived ( this , & CAchievementMgr : : Steam_OnUserStatsReceived ) ,
m_CallbackUserStatsStored ( this , & CAchievementMgr : : Steam_OnUserStatsStored )
# endif
{
for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS ; + + i )
{
SetDefLessFunc ( m_mapAchievement [ i ] ) ;
m_flLastClassChangeTime [ i ] = 0 ;
m_flTeamplayStartTime [ i ] = 0 ;
m_iMiniroundsCompleted [ i ] = 0 ;
m_bDirty [ i ] = false ;
m_AchievementsAwarded [ i ] . RemoveAll ( ) ;
m_AchievementsAwardedDuringCurrentGame [ i ] . RemoveAll ( ) ;
m_bUserSlotActive [ i ] = false ;
}
m_szMap [ 0 ] = 0 ;
m_bCheatsEverOn = false ;
m_flTimeLastUpload = 0 ;
2024-08-29 19:27:02 -04:00
m_flWaitingForStoreStatsCallback = 0.0f ;
m_bCallStoreStatsAfterCallback = false ;
2024-08-29 19:18:30 -04:00
# ifdef _X360
// Mark that we're not waiting for an async call to finish
m_pendingAchievementState . Purge ( ) ;
# endif // _X360
}
//-----------------------------------------------------------------------------
// Purpose: Initializer
//-----------------------------------------------------------------------------
bool CAchievementMgr : : Init ( )
{
// We can be created on either client (for multiplayer games) or server
// (for single player), so register ourselves with the engine so UI has a uniform place
// to go get the pointer to us
# ifdef _DEBUG
// There can be only one achievement manager instance; no one else should be registered
IAchievementMgr * pAchievementMgr = engine - > GetAchievementMgr ( ) ;
Assert ( NULL = = pAchievementMgr ) ;
# endif // _DEBUG
// register ourselves
engine - > SetAchievementMgr ( this ) ;
// register for events
# ifdef GAME_DLL
ListenForGameEvent ( " entity_killed " ) ;
ListenForGameEvent ( " game_init " ) ;
# else
ListenForGameEvent ( " player_death " ) ;
ListenForGameEvent ( " player_stats_updated " ) ;
ListenForGameEvent ( " achievement_write_failed " ) ;
for ( int hh = 0 ; hh < MAX_SPLITSCREEN_PLAYERS ; + + hh )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD ( hh ) ;
usermessages - > HookMessage ( " AchievementEvent " , MsgFunc_AchievementEvent ) ;
}
# endif // CLIENT_DLL
g_pMatchFramework - > GetEventsSubscription ( ) - > Subscribe ( this ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: called at init time after all systems are init'd. We have to
// do this in PostInit because the Steam app ID is not available earlier
//-----------------------------------------------------------------------------
void CAchievementMgr : : PostInit ( )
{
// get current game dir
const char * pGameDir = COM_GetModDirectory ( ) ;
CBaseAchievementHelper * pAchievementHelper = CBaseAchievementHelper : : s_pFirst ;
while ( pAchievementHelper )
{
for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS ; + + i )
{
// create and initialize all achievements and insert them in our maps
CBaseAchievement * pAchievement = pAchievementHelper - > m_pfnCreate ( ) ;
pAchievement - > m_pAchievementMgr = this ;
pAchievement - > Init ( ) ;
pAchievement - > CalcProgressMsgIncrement ( ) ;
pAchievement - > SetUserSlot ( i ) ;
// only add an achievement if it does not have a game filter (only compiled into the game it
// applies to, or truly cross-game) or, if it does have a game filter, the filter matches current game.
// (e.g. EP 1/2/... achievements are in shared binary but are game specific, they have a game filter for runtime check.)
const char * pGameDirFilter = pAchievement - > m_pGameDirFilter ;
if ( ! pGameDirFilter | | ( 0 = = Q_strcmp ( pGameDir , pGameDirFilter ) ) )
{
m_mapAchievement [ i ] . Insert ( pAchievement - > GetAchievementID ( ) , pAchievement ) ;
}
else
{
// achievement is not for this game, don't use it
delete pAchievement ;
}
}
pAchievementHelper = pAchievementHelper - > m_pNext ;
}
for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS ; + + i )
{
// Add each of the achievements to a CUtlVector ordered by achievement ID
FOR_EACH_MAP ( m_mapAchievement [ i ] , iter )
{
m_vecAchievement [ i ] . AddToTail ( m_mapAchievement [ i ] [ iter ] ) ;
}
m_vecAchievement [ i ] . Sort ( AchievementIDCompare ) ;
// Create the vector of achievements in display order
m_vecAchievementInOrder [ i ] . AddVectorToTail ( m_vecAchievement [ i ] ) ;
m_vecAchievementInOrder [ i ] . Sort ( AchievementOrderCompare ) ;
// Clear the progress and achieved data for each splitscreen player
ClearAchievementData ( i ) ;
}
if ( IsPC ( ) )
{
UserConnected ( STEAM_PLAYER_SLOT ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Shuts down the achievement manager
//-----------------------------------------------------------------------------
void CAchievementMgr : : Shutdown ( )
{
SaveGlobalState ( ) ;
for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS ; + + i )
{
FOR_EACH_MAP ( m_mapAchievement [ i ] , iter )
{
delete m_mapAchievement [ i ] [ iter ] ;
}
m_mapAchievement [ i ] . RemoveAll ( ) ;
m_vecAchievement [ i ] . RemoveAll ( ) ;
m_vecAchievementInOrder [ i ] . RemoveAll ( ) ;
m_vecKillEventListeners [ i ] . RemoveAll ( ) ;
m_vecMapEventListeners [ i ] . RemoveAll ( ) ;
m_vecComponentListeners [ i ] . RemoveAll ( ) ;
m_AchievementsAwarded [ i ] . RemoveAll ( ) ;
m_AchievementsAwardedDuringCurrentGame [ i ] . RemoveAll ( ) ;
}
g_pMatchFramework - > GetEventsSubscription ( ) - > Unsubscribe ( this ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Cleans up all achievements and then re-initializes them
//-----------------------------------------------------------------------------
void CAchievementMgr : : InitializeAchievements ( )
{
Shutdown ( ) ;
PostInit ( ) ;
}
# ifdef CLIENT_DLL
extern const ConVar * sv_cheats ;
# endif
//-----------------------------------------------------------------------------
// Purpose: Do per-frame handling
//-----------------------------------------------------------------------------
void CAchievementMgr : : Update ( float frametime )
{
# ifdef CLIENT_DLL
if ( ! sv_cheats )
{
sv_cheats = cvar - > FindVar ( " sv_cheats " ) ;
}
# endif
# ifndef _DEBUG
// keep track if cheats have ever been turned on during this level
if ( ! WereCheatsEverOn ( ) )
{
if ( sv_cheats & & sv_cheats - > GetBool ( ) )
{
m_bCheatsEverOn = true ;
}
}
# endif
# ifdef _X360
bool bWarningShown = false ;
for ( int i = m_pendingAchievementState . Count ( ) - 1 ; i > = 0 ; i - - ) // Iterate backwards to make deletion safe
{
// Check for a pending achievement write
uint nResultCode ;
int nReturn = xboxsystem - > GetOverlappedResult ( m_pendingAchievementState [ i ] . pOverlappedResult , & nResultCode , false ) ;
if ( nReturn = = ERROR_IO_PENDING | | nReturn = = ERROR_IO_INCOMPLETE )
continue ;
// Warn if the write failed
if ( nReturn ! = ERROR_SUCCESS )
{
if ( bWarningShown = = false )
{
// Create a game message to pop up a warning to the user
IGameEvent * event = gameeventmanager - > CreateEvent ( " achievement_write_failed " ) ;
if ( event )
{
gameeventmanager - > FireEvent ( event ) ;
bWarningShown = true ;
}
}
// We need to unaward the achievement in this case!
CBaseAchievement * pAchievement = GetAchievementByID ( m_pendingAchievementState [ i ] . nAchievementID , m_pendingAchievementState [ i ] . nUserSlot ) ;
if ( pAchievement ! = NULL )
{
pAchievement - > SetAchieved ( false ) ;
m_bDirty [ m_pendingAchievementState [ i ] . nUserSlot ] = true ;
m_AchievementsAwardedDuringCurrentGame - > FindAndRemove ( m_pendingAchievementState [ i ] . nAchievementID ) ;
// FIXME: This doesn't account for incremental progress, but if *will* re-achieve these if you get them again
}
}
// We've either succeeded or failed at this point, in both cases we don't care anymore!
xboxsystem - > ReleaseAsyncHandle ( m_pendingAchievementState [ i ] . pOverlappedResult ) ;
m_pendingAchievementState . FastRemove ( i ) ;
}
2024-08-29 19:27:02 -04:00
const float flStoreStatsTimeout = 10.0f ;
if ( m_flWaitingForStoreStatsCallback > 0.0f & & gpGlobals - > curtime > m_flWaitingForStoreStatsCallback + flStoreStatsTimeout )
{
m_flWaitingForStoreStatsCallback = 0.0f ;
}
2024-08-29 19:18:30 -04:00
# endif // _X360
}
//-----------------------------------------------------------------------------
// Purpose: called on level init
//-----------------------------------------------------------------------------
void CAchievementMgr : : LevelInitPreEntity ( )
{
m_bCheatsEverOn = false ;
# ifdef GAME_DLL
// For single-player games, achievement mgr must live on the server. (Only the server has detailed knowledge of game state.)
Assert ( ! GameRules ( ) - > IsMultiplayer ( ) ) ;
# else
// For multiplayer games, achievement mgr must live on the client. (Only the client can read/write player state from Steam/XBox Live.)
Assert ( GameRules ( ) - > IsMultiplayer ( ) ) ;
# endif
for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS ; + + i )
{
// clear list of achievements listening for events
m_vecKillEventListeners [ i ] . RemoveAll ( ) ;
m_vecMapEventListeners [ i ] . RemoveAll ( ) ;
m_vecComponentListeners [ i ] . RemoveAll ( ) ;
m_AchievementsAwarded [ i ] . RemoveAll ( ) ;
m_flLastClassChangeTime [ i ] = 0 ;
m_flTeamplayStartTime [ i ] = 0 ;
m_iMiniroundsCompleted [ i ] = 0 ;
}
// client and server have map names available in different forms (full path on client, just file base name on server),
// cache it in base file name form here so we don't have to have different code paths each time we access it
# ifdef CLIENT_DLL
Q_FileBase ( engine - > GetLevelName ( ) , m_szMap , ARRAYSIZE ( m_szMap ) ) ;
# else
Q_strncpy ( m_szMap , gpGlobals - > mapname . ToCStr ( ) , ARRAYSIZE ( m_szMap ) ) ;
# endif // CLIENT_DLL
if ( IsX360 ( ) )
{
// need to remove the .360 extension on the end of the map name
char * pExt = Q_stristr ( m_szMap , " .360 " ) ;
if ( pExt )
{
* pExt = ' \0 ' ;
}
}
for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS ; + + i )
{
// Don't bother listening if a slot is not active
if ( m_bUserSlotActive [ i ] )
{
// look through all achievements, see which ones we want to have listen for events
FOR_EACH_MAP ( m_mapAchievement [ i ] , iAchievement )
{
CBaseAchievement * pAchievement = m_mapAchievement [ i ] [ iAchievement ] ;
// if the achievement only applies to a specific map, and it's not the current map, skip it
const char * pMapNameFilter = pAchievement - > m_pMapNameFilter ;
if ( pMapNameFilter & & ( 0 ! = Q_strcmp ( m_szMap , pMapNameFilter ) ) )
continue ;
// if the achievement needs kill events, add it as a listener
if ( pAchievement - > GetFlags ( ) & ACH_LISTEN_KILL_EVENTS )
{
m_vecKillEventListeners [ i ] . AddToTail ( pAchievement ) ;
}
// if the achievement needs map events, add it as a listener
if ( pAchievement - > GetFlags ( ) & ACH_LISTEN_MAP_EVENTS )
{
m_vecMapEventListeners [ i ] . AddToTail ( pAchievement ) ;
}
// if the achievement needs map events, add it as a listener
if ( pAchievement - > GetFlags ( ) & ACH_LISTEN_COMPONENT_EVENTS )
{
m_vecComponentListeners [ i ] . AddToTail ( pAchievement ) ;
}
if ( pAchievement - > IsActive ( ) )
{
pAchievement - > ListenForEvents ( ) ;
}
}
m_flLevelInitTime [ i ] = gpGlobals - > curtime ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: called on level shutdown
//-----------------------------------------------------------------------------
void CAchievementMgr : : LevelShutdownPreEntity ( )
{
for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS ; + + i )
{
// make all achievements stop listening for events
FOR_EACH_MAP ( m_mapAchievement [ i ] , iAchievement )
{
CBaseAchievement * pAchievement = m_mapAchievement [ i ] [ iAchievement ] ;
pAchievement - > StopListeningForAllEvents ( ) ;
}
}
// save global state if we have any changes
SaveGlobalStateIfDirty ( ) ;
// 12/2/2008ywb: HACK, this needs to know the nUserSlot!!!
int nUserSlot = 0 ;
UploadUserData ( nUserSlot ) ;
}
//-----------------------------------------------------------------------------
// Purpose: returns achievement for specified ID
//-----------------------------------------------------------------------------
CBaseAchievement * CAchievementMgr : : GetAchievementByID ( int iAchievementID , int nUserSlot )
{
int iAchievement = m_mapAchievement [ nUserSlot ] . Find ( iAchievementID ) ;
if ( iAchievement ! = m_mapAchievement [ nUserSlot ] . InvalidIndex ( ) )
{
return m_mapAchievement [ nUserSlot ] [ iAchievement ] ;
}
return NULL ;
}
//-----------------------------------------------------------------------------
// Purpose: returns achievement with specified name. NOTE: this iterates through
// all achievements to find the name, intended for debugging purposes.
// Use GetAchievementByID for fast lookup.
//-----------------------------------------------------------------------------
CBaseAchievement * CAchievementMgr : : GetAchievementByName ( const char * pchName , int nUserSlot )
{
VPROF ( " GetAchievementByName " ) ;
FOR_EACH_MAP_FAST ( m_mapAchievement [ nUserSlot ] , i )
{
CBaseAchievement * pAchievement = m_mapAchievement [ nUserSlot ] [ i ] ;
if ( pAchievement & & 0 = = ( Q_stricmp ( pchName , pAchievement - > GetName ( ) ) ) )
return pAchievement ;
}
return NULL ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if the achievement with the specified name has been achieved
//-----------------------------------------------------------------------------
bool CAchievementMgr : : HasAchieved ( const char * pchName , int nUserSlot )
{
CBaseAchievement * pAchievement = GetAchievementByName ( pchName , nUserSlot ) ;
if ( pAchievement )
return pAchievement - > IsAchieved ( ) ;
return false ;
}
//-----------------------------------------------------------------------------
// Purpose: downloads user data from Steam or XBox Live
//-----------------------------------------------------------------------------
void CAchievementMgr : : UserDisconnected ( int nUserSlot )
{
m_bUserSlotActive [ nUserSlot ] = false ;
ClearAchievementData ( nUserSlot ) ;
}
//-----------------------------------------------------------------------------
// Read our achievement data from the title data.
//-----------------------------------------------------------------------------
void CAchievementMgr : : ReadAchievementsFromTitleData ( int iController , int iSlot )
{
IPlayerLocal * pPlayer = g_pMatchFramework - > GetMatchSystem ( ) - > GetPlayerManager ( ) - > GetLocalPlayer ( iController ) ;
if ( ! pPlayer )
return ;
// Set the incremental achievement progress and components
for ( int i = 0 ; i < m_vecAchievement [ iSlot ] . Count ( ) ; + + i )
{
CBaseAchievement * pAchievement = m_vecAchievement [ iSlot ] [ i ] ;
if ( pAchievement )
{
pAchievement - > ReadProgress ( pPlayer ) ;
}
}
/*
//DISABLED: Avatar award support
for ( int i = 0 ; i < m_vecAward [ iSlot ] . Count ( ) ; + + i )
{
CBaseAchievement * pAward = m_vecAward [ iSlot ] [ i ] ;
if ( pAward )
{
pAward - > ReadProgress ( pPlayer ) ;
}
}
*/
}
//-----------------------------------------------------------------------------
// Purpose: downloads user data from Steam or XBox Live
//-----------------------------------------------------------------------------
void CAchievementMgr : : UserConnected ( int nUserSlot )
{
# ifdef CLIENT_DLL
if ( IsPC ( ) )
{
// ASSERT( STEAM_PLAYER_SLOT == nUserSlot )
if ( steamapicontext - > SteamUserStats ( ) )
{
// request stat download; will get called back at OnUserStatsReceived when complete
steamapicontext - > SteamUserStats ( ) - > RequestCurrentStats ( ) ;
}
m_bUserSlotActive [ STEAM_PLAYER_SLOT ] = true ;
}
else if ( IsX360 ( ) )
{
# if defined( _X360 )
if ( XBX_GetUserIsGuest ( nUserSlot ) )
return ;
const int iController = XBX_GetUserId ( nUserSlot ) ;
if ( iController = = XBX_INVALID_USER_ID )
return ;
if ( XUserGetSigninState ( iController ) = = eXUserSigninState_NotSignedIn )
return ;
m_bUserSlotActive [ nUserSlot ] = true ;
// Download achievements from XBox Live
bool bDownloadSuccessful = true ;
const int nTotalAchievements = GetAchievementCount ( ) ;
uint bytes ;
int ret = xboxsystem - > EnumerateAchievements ( iController , 0 , 0 , nTotalAchievements , & bytes , 0 , false ) ;
if ( ret ! = ERROR_SUCCESS )
{
Warning ( " Enumerate Achievements failed! Error %d " , ret ) ;
bDownloadSuccessful = false ;
}
// Enumerate the achievements from Live
void * pBuffer = new byte [ bytes ] ;
if ( bDownloadSuccessful )
{
ret = xboxsystem - > EnumerateAchievements ( iController , 0 , 0 , nTotalAchievements , pBuffer , bytes , false ) ;
if ( ret ! = nTotalAchievements )
{
Warning ( " Enumerate Achievements failed! Error %d " , ret ) ;
bDownloadSuccessful = false ;
}
}
if ( bDownloadSuccessful )
{
// Give live a chance to mark achievements as unlocked, in case the achievement manager
// wasn't able to get that data (storage device missing, read failure, etc)
XACHIEVEMENT_DETAILS * pXboxAchievements = ( XACHIEVEMENT_DETAILS * ) pBuffer ;
for ( int i = 0 ; i < nTotalAchievements ; + + i )
{
CBaseAchievement * pAchievement = GetAchievementByID ( pXboxAchievements [ i ] . dwId , nUserSlot ) ;
if ( ! pAchievement )
continue ;
// Give Live a chance to claim the achievement as unlocked
if ( AchievementEarned ( pXboxAchievements [ i ] . dwFlags ) )
{
pAchievement - > SetAchieved ( true ) ;
}
}
}
delete pBuffer ;
# endif // X360
}
# endif // CLIENT_DLL
}
const char * COM_GetModDirectory ( )
{
static char modDir [ MAX_PATH ] ;
if ( Q_strlen ( modDir ) = = 0 )
{
const char * gamedir = CommandLine ( ) - > ParmValue ( " -game " , CommandLine ( ) - > ParmValue ( " -defaultgamedir " , " hl2 " ) ) ;
Q_strncpy ( modDir , gamedir , sizeof ( modDir ) ) ;
if ( strchr ( modDir , ' / ' ) | | strchr ( modDir , ' \\ ' ) )
{
Q_StripLastDir ( modDir , sizeof ( modDir ) ) ;
int dirlen = Q_strlen ( modDir ) ;
Q_strncpy ( modDir , gamedir + dirlen , sizeof ( modDir ) - dirlen ) ;
}
}
return modDir ;
}
//-----------------------------------------------------------------------------
// Purpose: Uploads user data to Steam or XBox Live
//-----------------------------------------------------------------------------
void CAchievementMgr : : UploadUserData ( int nUserSlot )
{
# ifdef CLIENT_DLL
if ( IsPC ( ) & & ( nUserSlot = = STEAM_PLAYER_SLOT ) )
{
if ( steamapicontext - > SteamUserStats ( ) )
{
2024-08-29 19:27:02 -04:00
if ( m_flWaitingForStoreStatsCallback > 0.0f )
{
m_bCallStoreStatsAfterCallback = true ;
return ;
}
2024-08-29 19:18:30 -04:00
// Upload current Steam client achievements & stats state to Steam. Will get called back at OnUserStatsStored when complete.
// Only values previously set via SteamUserStats() get uploaded
2024-08-29 19:27:02 -04:00
if ( steamapicontext - > SteamUserStats ( ) - > StoreStats ( ) )
{
m_flWaitingForStoreStatsCallback = gpGlobals - > curtime ;
}
2024-08-29 19:18:30 -04:00
m_flTimeLastUpload = Plat_FloatTime ( ) ;
}
}
# endif
}
//-----------------------------------------------------------------------------
// Purpose: saves global state to file
//-----------------------------------------------------------------------------
void CAchievementMgr : : SaveGlobalState ( )
{
if ( IsPC ( ) )
{
VPROF_BUDGET ( " CAchievementMgr::SaveGlobalState " , " Achievements " ) ;
// FIXMEL4DTOMAINMERGE
// Requires IMatchmaking!!!
#if 0
TitleData2 achievementTitleData ;
// Clear the data structure out
memset ( & achievementTitleData . sizeofachievements , 0 , sizeof ( achievementTitleData . sizeofachievements ) ) ;
for ( int i = 0 ; i < m_vecAchievement [ STEAM_PLAYER_SLOT ] . Count ( ) ; + + i )
{
CBaseAchievement * pAchievement = m_vecAchievement [ STEAM_PLAYER_SLOT ] [ i ] ;
achievementTitleData . achievements . uAchievementCount [ i ] = ( uint32 ) pAchievement - > GetCount ( ) ;
if ( pAchievement - > HasComponents ( ) )
{
achievementTitleData . achievements . uAchievementComponentBits [ i ] = pAchievement - > GetComponentBits ( ) ;
}
}
matchmaking - > UpdatePlayerData2 ( 0 , achievementTitleData , TitleData2 : : CONTENTTYPE_ACHIEVEMENTS ) ;
# endif
m_bDirty [ 0 ] = false ;
}
else if ( IsX360 ( ) )
{
# ifdef _X360
# if 1
# pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabling XBox achievement code for now. Easier than real fixes so we can get 360 compiling" )
# else
for ( int j = 0 ; j < MAX_SPLITSCREEN_PLAYERS ; + + j )
{
if ( IsUserConnected ( j ) )
{
TitleData2 achievementTitleData ;
// Clear the data structure out
memset ( & achievementTitleData . sizeofachievements , 0 , sizeof ( achievementTitleData . sizeofachievements ) ) ;
for ( int i = 0 ; i < m_vecAchievement [ j ] . Count ( ) ; + + i )
{
CBaseAchievement * pAchievement = m_vecAchievement [ j ] [ i ] ;
achievementTitleData . achievements . uAchievementCount [ i ] = ( uint32 ) pAchievement - > GetCount ( ) ;
if ( pAchievement - > HasComponents ( ) )
{
achievementTitleData . achievements . uAchievementComponentBits [ i ] = pAchievement - > GetComponentBits ( ) ;
}
}
matchmaking - > UpdatePlayerData2 ( XBX_GetUserId ( j ) , achievementTitleData , TitleData2 : : CONTENTTYPE_ACHIEVEMENTS ) ;
}
}
# endif
# endif
}
}
//-----------------------------------------------------------------------------
// Purpose: saves global state to file if there have been any changes
//-----------------------------------------------------------------------------
void CAchievementMgr : : SaveGlobalStateIfDirty ( )
{
if ( m_bDirty )
{
SaveGlobalState ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: awards specified achievement
//-----------------------------------------------------------------------------
void CAchievementMgr : : AwardAchievement ( int iAchievementID , int nUserSlot )
{
# ifdef CLIENT_DLL
CBaseAchievement * pAchievement = GetAchievementByID ( iAchievementID , nUserSlot ) ;
Assert ( pAchievement ) ;
if ( ! pAchievement )
return ;
if ( ! CheckAchievementsEnabled ( ) )
{
Msg ( " Achievements disabled, ignoring achievement unlock for %s \n " , pAchievement - > GetName ( ) ) ;
return ;
}
if ( pAchievement - > IsAchieved ( ) )
{
if ( cc_achievement_debug . GetInt ( ) > 0 )
{
Msg ( " Achievement award called but already achieved: %s \n " , pAchievement - > GetName ( ) ) ;
}
return ;
}
pAchievement - > SetAchieved ( true ) ;
if ( cc_achievement_debug . GetInt ( ) > 0 )
{
Msg ( " Achievement awarded: %s \n " , pAchievement - > GetName ( ) ) ;
}
// save state at next good opportunity. (Don't do it immediately, may hitch at bad time.)
m_bDirty [ nUserSlot ] = true ;
if ( IsPC ( ) )
{
if ( steamapicontext - > SteamUserStats ( ) )
{
VPROF_BUDGET ( " AwardAchievement " , VPROF_BUDGETGROUP_STEAM ) ;
// set this achieved in the Steam client
bool bRet = steamapicontext - > SteamUserStats ( ) - > SetAchievement ( pAchievement - > GetName ( ) ) ;
// Assert( bRet );
if ( bRet )
{
// upload achievement to steam
UploadUserData ( nUserSlot ) ;
m_AchievementsAwarded [ nUserSlot ] . AddToTail ( iAchievementID ) ;
}
}
}
else if ( IsX360 ( ) )
{
# ifdef _X360
if ( xboxsystem )
{
PendingAchievementInfo_t pendingAchievementState = { iAchievementID , nUserSlot , NULL } ;
xboxsystem - > AwardAchievement ( XBX_GetUserId ( nUserSlot ) , iAchievementID , & pendingAchievementState . pOverlappedResult ) ;
// Save off the results for checking later
m_pendingAchievementState . AddToTail ( pendingAchievementState ) ;
}
# endif
}
SaveGlobalStateIfDirty ( ) ;
// Add this one to the list of achievements earned during current session
m_AchievementsAwardedDuringCurrentGame [ nUserSlot ] . AddToTail ( iAchievementID ) ;
# endif // CLIENT_DLL
}
//-----------------------------------------------------------------------------
// Purpose: updates specified achievement
//-----------------------------------------------------------------------------
void CAchievementMgr : : UpdateAchievement ( int iAchievementID , int nData , int nUserSlot )
{
CBaseAchievement * pAchievement = GetAchievementByID ( iAchievementID , nUserSlot ) ;
Assert ( pAchievement ) ;
if ( ! pAchievement )
return ;
if ( ! CheckAchievementsEnabled ( ) )
{
Msg ( " Achievements disabled, ignoring achievement update for %s \n " , pAchievement - > GetName ( ) ) ;
return ;
}
if ( pAchievement - > IsAchieved ( ) )
{
if ( cc_achievement_debug . GetInt ( ) > 0 )
{
Msg ( " Achievement update called but already achieved: %s \n " , pAchievement - > GetName ( ) ) ;
}
return ;
}
pAchievement - > UpdateAchievement ( nData ) ;
}
//-----------------------------------------------------------------------------
// Purpose: clears state for all achievements
//-----------------------------------------------------------------------------
void CAchievementMgr : : PreRestoreSavedGame ( )
{
for ( int j = 0 ; j < MAX_SPLITSCREEN_PLAYERS ; + + j )
{
FOR_EACH_MAP ( m_mapAchievement [ j ] , i )
{
m_mapAchievement [ j ] [ i ] - > PreRestoreSavedGame ( ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: clears state for all achievements
//-----------------------------------------------------------------------------
void CAchievementMgr : : PostRestoreSavedGame ( )
{
for ( int j = 0 ; j < MAX_SPLITSCREEN_PLAYERS ; + + j )
{
FOR_EACH_MAP ( m_mapAchievement [ j ] , i )
{
m_mapAchievement [ j ] [ i ] - > PostRestoreSavedGame ( ) ;
}
}
}
extern bool IsInCommentaryMode ( void ) ;
ConVar cc_achievement_disable ( " achievement_disable " , " 0 " , FCVAR_CHEAT | FCVAR_REPLICATED , " Turn off achievements. " ) ;
//-----------------------------------------------------------------------------
// Purpose: checks if achievements are enabled
//-----------------------------------------------------------------------------
bool CAchievementMgr : : CheckAchievementsEnabled ( )
{
// if PC, Steam must be running and user logged in
if ( cc_achievement_disable . GetBool ( ) )
return false ;
if ( IsPC ( ) & & ! LoggedIntoSteam ( ) )
{
Msg ( " Achievements disabled: Steam not running. \n " ) ;
return false ;
}
//No achievements in demo version.
# ifdef _DEMO
return false ;
# endif
# if defined( _X360 )
# if 1
# pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabling XBox achievement code for now. Easier than real fixes so we can get 360 compiling" )
Msg ( " Achievements disabled: 360 broken for now. \n " ) ;
return false ;
# else
uint state = XUserGetSigninState ( XBX_GetActiveUserId ( ) ) ;
if ( state = = eXUserSigninState_NotSignedIn )
{
Msg ( " Achievements disabled: not signed in to XBox user account. \n " ) ;
return false ;
}
# endif
# endif
// can't be in commentary mode, user is invincible
if ( IsInCommentaryMode ( ) )
{
Msg ( " Achievements disabled: in commentary mode. \n " ) ;
return false ;
}
# ifdef CLIENT_DLL
// achievements disabled if playing demo (Playback demo)
if ( engine - > IsPlayingDemo ( ) )
{
Msg ( " Achievements disabled: demo playing. \n " ) ;
return false ;
}
# endif // CLIENT_DLL
if ( IsPC ( ) )
{
// Don't award achievements if cheats are turned on.
if ( WereCheatsEverOn ( ) )
{
// Cheats get turned on automatically if you run with -dev which many people do internally, so allow cheats if developer is turned on and we're not running
// on Steam public
# ifdef CLIENT_DLL
if ( ( developer . GetInt ( ) = = 0 ) | | ! steamapicontext - > SteamUtils ( ) | | ( k_EUniversePublic = = steamapicontext - > SteamUtils ( ) - > GetConnectedUniverse ( ) ) )
# else
if ( developer . GetInt ( ) = = 0 )
# endif
{
Msg ( " Achievements disabled: cheats turned on in this app session. \n " ) ;
return false ;
}
}
}
# ifdef INFESTED_DLL
# ifndef _DEBUG
// no achievements in singleplayer
if ( gpGlobals - > maxClients < = 1 )
{
DevMsg ( " Achievements disabled in singleplayer. \n " ) ;
return false ;
}
# endif
# endif
return true ;
}
# ifdef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: Returns the whether all of the local player's team mates are
// on her friends list, and if there are at least the specified # of
// teammates. Involves cross-process calls to Steam so this is mildly
// expensive, don't call this every frame.
//-----------------------------------------------------------------------------
bool CalcPlayersOnFriendsList ( int iMinFriends )
{
// Got message during connection
if ( ! g_PR )
return false ;
Assert ( g_pGameRules - > IsMultiplayer ( ) ) ;
// Do a cheap rejection: check teammate count first to see if we even need to bother checking w/Steam
// Subtract 1 for the local player.
if ( CalcPlayerCount ( ) - 1 < iMinFriends )
return false ;
// determine local player team
int iLocalPlayerIndex = GetLocalPlayerIndex ( ) ;
uint64 XPlayerUid = 0 ;
if ( IsPC ( ) )
{
if ( ! steamapicontext - > SteamFriends ( ) | | ! steamapicontext - > SteamUtils ( ) | | ! g_pGameRules - > IsMultiplayer ( ) )
return false ;
}
else if ( IsX360 ( ) )
{
if ( ! g_pMatchFramework )
return false ;
XPlayerUid = XBX_GetActiveUserId ( ) ;
}
else
{
// other platforms...?
return false ;
}
// Loop through the players
int iTotalFriends = 0 ;
for ( int iPlayerIndex = 1 ; iPlayerIndex < = MAX_PLAYERS ; iPlayerIndex + + )
{
// find all players who are on the local player's team
if ( ( iPlayerIndex ! = iLocalPlayerIndex ) & & ( g_PR - > IsConnected ( iPlayerIndex ) ) )
{
if ( IsPC ( ) )
{
player_info_t pi ;
if ( ! engine - > GetPlayerInfo ( iPlayerIndex , & pi ) )
continue ;
if ( ! pi . friendsID )
continue ;
// check and see if they're on the local player's friends list
CSteamID steamID ( pi . friendsID , 1 , steamapicontext - > SteamUtils ( ) - > GetConnectedUniverse ( ) , k_EAccountTypeIndividual ) ;
if ( ! steamapicontext - > SteamFriends ( ) - > HasFriend ( steamID , /*k_EFriendFlagImmediate*/ 0x04 ) )
continue ;
}
else if ( IsX360 ( ) )
{
# ifdef _X360
# if 1
# pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabling XBox achievement code for now. Easier than real fixes so we can get 360 compiling" )
# else
// check and see if they're on the local player's friends list
BOOL bFriend = FALSE ;
XUserAreUsersFriends ( XPlayerUid , & pi . xuid , 1 , & bFriend , NULL ) ;
if ( ! bFriend )
continue ;
iTotalFriends + + ;
# endif
# endif
}
}
}
return ( iTotalFriends > = iMinFriends ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns whether there are a specified # of teammates who all belong
// to same clan as local player. Involves cross-process calls to Steam
// so this is mildly expensive, don't call this every frame.
//-----------------------------------------------------------------------------
bool CalcHasNumClanPlayers ( int iClanTeammates )
{
Assert ( g_pGameRules - > IsMultiplayer ( ) ) ;
if ( IsPC ( ) )
{
// Do a cheap rejection: check teammate count first to see if we even need to bother checking w/Steam
// Subtract 1 for the local player.
if ( CalcPlayerCount ( ) - 1 < iClanTeammates )
return false ;
if ( ! steamapicontext - > SteamFriends ( ) | | ! steamapicontext - > SteamUtils ( ) | | ! g_pGameRules - > IsMultiplayer ( ) )
return false ;
// determine local player team
int iLocalPlayerIndex = GetLocalPlayerIndex ( ) ;
for ( int iClan = 0 ; iClan < steamapicontext - > SteamFriends ( ) - > GetClanCount ( ) ; iClan + + )
{
int iClanMembersOnTeam = 0 ;
CSteamID clanID = steamapicontext - > SteamFriends ( ) - > GetClanByIndex ( iClan ) ;
// enumerate all players
for ( int iPlayerIndex = 1 ; iPlayerIndex < = MAX_PLAYERS ; iPlayerIndex + + )
{
if ( ( iPlayerIndex ! = iLocalPlayerIndex ) & & ( g_PR - > IsConnected ( iPlayerIndex ) ) )
{
player_info_t pi ;
if ( engine - > GetPlayerInfo ( iPlayerIndex , & pi ) & & ( pi . friendsID ) )
{
// check and see if they're on the local player's friends list
CSteamID steamID ( pi . friendsID , 1 , steamapicontext - > SteamUtils ( ) - > GetConnectedUniverse ( ) , k_EAccountTypeIndividual ) ;
if ( steamapicontext - > SteamFriends ( ) - > IsUserInSource ( steamID , clanID ) )
{
iClanMembersOnTeam + + ;
if ( iClanMembersOnTeam = = iClanTeammates )
return true ;
}
}
}
}
}
return false ;
}
else if ( IsX360 ( ) )
{
// TODO: implement for 360
return false ;
}
else
{
// other platforms...?
return false ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns the # of teammates of the local player
//-----------------------------------------------------------------------------
int CalcTeammateCount ( )
{
Assert ( g_pGameRules - > IsMultiplayer ( ) ) ;
// determine local player team
int iLocalPlayerIndex = GetLocalPlayerIndex ( ) ;
int iLocalPlayerTeam = g_PR - > GetTeam ( iLocalPlayerIndex ) ;
int iNumTeammates = 0 ;
for ( int iPlayerIndex = 1 ; iPlayerIndex < = MAX_PLAYERS ; iPlayerIndex + + )
{
// find all players who are on the local player's team
if ( ( iPlayerIndex ! = iLocalPlayerIndex ) & & ( g_PR - > IsConnected ( iPlayerIndex ) ) & & ( g_PR - > GetTeam ( iPlayerIndex ) = = iLocalPlayerTeam ) )
{
iNumTeammates + + ;
}
}
return iNumTeammates ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the # of teammates of the local player
//-----------------------------------------------------------------------------
int CalcPlayerCount ( )
{
int iCount = 0 ;
for ( int iPlayerIndex = 1 ; iPlayerIndex < = MAX_PLAYERS ; iPlayerIndex + + )
{
// find all players who are on the local player's team
if ( g_PR - > IsConnected ( iPlayerIndex ) )
{
iCount + + ;
}
}
return iCount ;
}
# endif // CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: Resets all achievements. For debugging purposes only
//-----------------------------------------------------------------------------
void CAchievementMgr : : ResetAchievements ( )
{
# ifdef CLIENT_DLL
if ( ! IsPC ( ) )
{
DevMsg ( " Only available on PC \n " ) ;
return ;
}
if ( ! LoggedIntoSteam ( ) )
{
Msg ( " Steam not running, achievements disabled. Cannot reset achievements. \n " ) ;
return ;
}
FOR_EACH_MAP ( m_mapAchievement [ STEAM_PLAYER_SLOT ] , i )
{
CBaseAchievement * pAchievement = m_mapAchievement [ STEAM_PLAYER_SLOT ] [ i ] ;
ResetAchievement_Internal ( pAchievement ) ;
}
2024-08-29 19:27:02 -04:00
UploadUserData ( STEAM_PLAYER_SLOT ) ;
2024-08-29 19:18:30 -04:00
SaveGlobalState ( ) ;
# endif // CLIENT_DLL
}
void CAchievementMgr : : ResetAchievement ( int iAchievementID )
{
# ifdef CLIENT_DLL
if ( ! IsPC ( ) )
{
DevMsg ( " Only available on PC \n " ) ;
return ;
}
if ( ! LoggedIntoSteam ( ) )
{
Msg ( " Steam not running, achievements disabled. Cannot reset achievements. \n " ) ;
return ;
}
CBaseAchievement * pAchievement = GetAchievementByID ( iAchievementID , STEAM_PLAYER_SLOT ) ;
Assert ( pAchievement ) ;
if ( pAchievement )
{
ResetAchievement_Internal ( pAchievement ) ;
2024-08-29 19:27:02 -04:00
UploadUserData ( STEAM_PLAYER_SLOT ) ;
2024-08-29 19:18:30 -04:00
SaveGlobalState ( ) ;
}
# endif // CLIENT_DLL
}
//-----------------------------------------------------------------------------
// Purpose: Resets all achievements. For debugging purposes only
//-----------------------------------------------------------------------------
void CAchievementMgr : : PrintAchievementStatus ( )
{
if ( IsPC ( ) & & ! LoggedIntoSteam ( ) )
{
Msg ( " Steam not running, achievements disabled. Cannot view or unlock achievements. \n " ) ;
return ;
}
Msg ( " %42s %-20s %s \n " , " Name: " , " Status: " , " Point value: " ) ;
int iTotalAchievements = 0 , iTotalPoints = 0 ;
FOR_EACH_MAP ( m_mapAchievement [ STEAM_PLAYER_SLOT ] , i )
{
CBaseAchievement * pAchievement = m_mapAchievement [ STEAM_PLAYER_SLOT ] [ i ] ;
Msg ( " %42s " , pAchievement - > GetName ( ) ) ;
CFailableAchievement * pFailableAchievement = dynamic_cast < CFailableAchievement * > ( pAchievement ) ;
if ( pAchievement - > IsAchieved ( ) )
{
Msg ( " %-20s " , " ACHIEVED " ) ;
}
else if ( pFailableAchievement & & pFailableAchievement - > IsFailed ( ) )
{
Msg ( " %-20s " , " FAILED " ) ;
}
else
{
char szBuf [ 255 ] ;
Q_snprintf ( szBuf , ARRAYSIZE ( szBuf ) , " (%d/%d)%s " , pAchievement - > GetCount ( ) , pAchievement - > GetGoal ( ) ,
pAchievement - > IsActive ( ) ? " " : " (inactive) " ) ;
Msg ( " %-20s " , szBuf ) ;
}
Msg ( " %d " , pAchievement - > GetPointValue ( ) ) ;
pAchievement - > PrintAdditionalStatus ( ) ;
Msg ( " \n " ) ;
iTotalAchievements + + ;
iTotalPoints + = pAchievement - > GetPointValue ( ) ;
}
Msg ( " Total achievements: %d Total possible points: %d \n " , iTotalAchievements , iTotalPoints ) ;
}
//-----------------------------------------------------------------------------
// Purpose: called when a game event is fired
//-----------------------------------------------------------------------------
void CAchievementMgr : : FireGameEvent ( IGameEvent * event )
{
# ifdef CLIENT_DLL
int nSplitScreenPlayer = event - > GetInt ( " splitscreenplayer " ) ;
ACTIVE_SPLITSCREEN_PLAYER_GUARD ( nSplitScreenPlayer ) ;
# endif
VPROF_ ( " CAchievementMgr::FireGameEvent " , 1 , VPROF_BUDGETGROUP_STEAM , false , 0 ) ;
const char * name = event - > GetName ( ) ;
if ( 0 = = Q_strcmp ( name , " entity_killed " ) )
{
# ifdef GAME_DLL
CBaseEntity * pVictim = UTIL_EntityByIndex ( event - > GetInt ( " entindex_killed " , 0 ) ) ;
CBaseEntity * pAttacker = UTIL_EntityByIndex ( event - > GetInt ( " entindex_attacker " , 0 ) ) ;
CBaseEntity * pInflictor = UTIL_EntityByIndex ( event - > GetInt ( " entindex_inflictor " , 0 ) ) ;
OnKillEvent ( pVictim , pAttacker , pInflictor , event ) ;
# endif // GAME_DLL
}
else if ( 0 = = Q_strcmp ( name , " game_init " ) )
{
# ifdef GAME_DLL
// clear all state as though we were loading a saved game, but without loading the game
PreRestoreSavedGame ( ) ;
PostRestoreSavedGame ( ) ;
# endif // GAME_DLL
}
# ifdef CLIENT_DLL
else if ( 0 = = Q_strcmp ( name , " player_death " ) )
{
CBaseEntity * pVictim = ClientEntityList ( ) . GetEnt ( engine - > GetPlayerForUserID ( event - > GetInt ( " userid " ) ) ) ;
CBaseEntity * pAttacker = ClientEntityList ( ) . GetEnt ( engine - > GetPlayerForUserID ( event - > GetInt ( " attacker " ) ) ) ;
OnKillEvent ( pVictim , pAttacker , NULL , event ) ;
}
else if ( 0 = = Q_strcmp ( name , " localplayer_changeclass " ) )
{
// keep track of when the player last changed class
m_flLastClassChangeTime [ nSplitScreenPlayer ] = gpGlobals - > curtime ;
}
else if ( 0 = = Q_strcmp ( name , " localplayer_changeteam " ) )
{
// keep track of the time of transitions to and from a game team
C_BasePlayer * pLocalPlayer = C_BasePlayer : : GetLocalPlayer ( ) ;
if ( pLocalPlayer )
{
int iTeam = pLocalPlayer - > GetTeamNumber ( ) ;
if ( iTeam > TEAM_SPECTATOR )
{
if ( 0 = = m_flTeamplayStartTime [ nSplitScreenPlayer ] )
{
// player transitioned from no/spectator team to a game team, mark the time
m_flTeamplayStartTime [ nSplitScreenPlayer ] = gpGlobals - > curtime ;
}
}
else
{
// player transitioned to no/spectator team, clear the teamplay start time
m_flTeamplayStartTime [ nSplitScreenPlayer ] = 0 ;
}
}
}
else if ( 0 = = Q_strcmp ( name , " teamplay_round_start " ) )
{
if ( event - > GetBool ( " full_reset " ) )
{
// we're starting a full round, clear miniround count
m_iMiniroundsCompleted [ nSplitScreenPlayer ] = 0 ;
}
}
else if ( 0 = = Q_strcmp ( name , " teamplay_round_win " ) )
{
if ( false = = event - > GetBool ( " full_round " , true ) )
{
// we just finished a miniround but the round is continuing, increment miniround count
m_iMiniroundsCompleted [ nSplitScreenPlayer ] + + ;
}
}
else if ( 0 = = Q_strcmp ( name , " player_stats_updated " ) )
{
FOR_EACH_MAP ( m_mapAchievement [ nSplitScreenPlayer ] , i )
{
CBaseAchievement * pAchievement = m_mapAchievement [ nSplitScreenPlayer ] [ i ] ;
pAchievement - > OnPlayerStatsUpdate ( ) ;
}
}
else if ( 0 = = Q_strcmp ( name , " achievement_write_failed " ) )
{
# ifdef _X360
// We didn't succeed and we're not waiting, so we failed
g_pMatchFramework - > GetEventsSubscription ( ) - > BroadcastEvent ( new KeyValues (
" OnProfileUnavailable " , " iController " , XBX_GetUserId ( nSplitScreenPlayer ) ) ) ;
# endif
}
# endif // CLIENT_DLL
}
//-----------------------------------------------------------------------------
// Purpose: called when a player or character has been killed
//-----------------------------------------------------------------------------
void CAchievementMgr : : OnKillEvent ( CBaseEntity * pVictim , CBaseEntity * pAttacker , CBaseEntity * pInflictor , IGameEvent * event )
{
# ifdef CLIENT_DLL
int nSplitScreenPlayer = SINGLE_PLAYER_SLOT ;
nSplitScreenPlayer = event - > GetInt ( " splitscreenplayer " ) ;
ACTIVE_SPLITSCREEN_PLAYER_GUARD ( nSplitScreenPlayer ) ;
# endif
// can have a NULL victim on client if victim has never entered local player's PVS
if ( ! pVictim )
return ;
// if single-player game, calculate if the attacker is the local player and if the victim is the player enemy
bool bAttackerIsPlayer = false ;
bool bVictimIsPlayerEnemy = false ;
# ifdef GAME_DLL
if ( ! g_pGameRules - > IsMultiplayer ( ) )
{
CBasePlayer * pLocalPlayer = UTIL_GetLocalPlayer ( ) ;
if ( pLocalPlayer )
{
if ( pAttacker = = pLocalPlayer )
{
bAttackerIsPlayer = true ;
}
CBaseCombatCharacter * pBCC = dynamic_cast < CBaseCombatCharacter * > ( pVictim ) ;
if ( pBCC & & ( D_HT = = pBCC - > IRelationType ( pLocalPlayer ) ) )
{
bVictimIsPlayerEnemy = true ;
}
}
}
# else
C_BasePlayer * pLocalPlayer = C_BasePlayer : : GetLocalPlayer ( ) ;
bVictimIsPlayerEnemy = ! pLocalPlayer - > InSameTeam ( pVictim ) ;
if ( pAttacker = = pLocalPlayer )
{
bAttackerIsPlayer = true ;
}
# endif // GAME_DLL
for ( int j = 0 ; j < MAX_SPLITSCREEN_PLAYERS ; + + j )
{
// look through all the kill event listeners and notify any achievements whose filters we pass
FOR_EACH_VEC ( m_vecKillEventListeners [ j ] , iAchievement )
{
CBaseAchievement * pAchievement = m_vecKillEventListeners [ j ] [ iAchievement ] ;
if ( ! pAchievement - > IsActive ( ) )
continue ;
// if this achievement only looks for kills where attacker is player and that is not the case here, skip this achievement
if ( ( pAchievement - > GetFlags ( ) & ACH_FILTER_ATTACKER_IS_PLAYER ) & & ! bAttackerIsPlayer )
continue ;
// if this achievement only looks for kills where victim is killer enemy and that is not the case here, skip this achievement
if ( ( pAchievement - > GetFlags ( ) & ACH_FILTER_VICTIM_IS_PLAYER_ENEMY ) & & ! bVictimIsPlayerEnemy )
continue ;
# if GAME_DLL
// if this achievement only looks for a particular victim class name and this victim is a different class, skip this achievement
const char * pVictimClassNameFilter = pAchievement - > m_pVictimClassNameFilter ;
if ( pVictimClassNameFilter & & ! pVictim - > ClassMatches ( pVictimClassNameFilter ) )
continue ;
// if this achievement only looks for a particular inflictor class name and this inflictor is a different class, skip this achievement
const char * pInflictorClassNameFilter = pAchievement - > m_pInflictorClassNameFilter ;
if ( pInflictorClassNameFilter & & ( ( NULL = = pInflictor ) | | ! pInflictor - > ClassMatches ( pInflictorClassNameFilter ) ) )
continue ;
// if this achievement only looks for a particular attacker class name and this attacker is a different class, skip this achievement
const char * pAttackerClassNameFilter = pAchievement - > m_pAttackerClassNameFilter ;
if ( pAttackerClassNameFilter & & ( ( NULL = = pAttacker ) | | ! pAttacker - > ClassMatches ( pAttackerClassNameFilter ) ) )
continue ;
// if this achievement only looks for a particular inflictor entity name and this inflictor has a different name, skip this achievement
const char * pInflictorEntityNameFilter = pAchievement - > m_pInflictorEntityNameFilter ;
if ( pInflictorEntityNameFilter & & ( ( NULL = = pInflictor ) | | ! pInflictor - > NameMatches ( pInflictorEntityNameFilter ) ) )
continue ;
# endif // GAME_DLL
// we pass all filters for this achievement, notify the achievement of the kill
pAchievement - > Event_EntityKilled ( pVictim , pAttacker , pInflictor , event ) ;
}
}
}
void CAchievementMgr : : OnAchievementEvent ( int iAchievementID , int nUserSlot )
{
// handle event for specific achievement
CBaseAchievement * pAchievement = GetAchievementByID ( iAchievementID , nUserSlot ) ;
Assert ( pAchievement ) ;
if ( pAchievement )
{
if ( ! pAchievement - > IsAchieved ( ) )
{
pAchievement - > IncrementCount ( ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: called when a map-fired achievement event occurs
//-----------------------------------------------------------------------------
void CAchievementMgr : : OnMapEvent ( const char * pchEventName , int nUserSlot )
{
Assert ( pchEventName & & * pchEventName ) ;
if ( ! pchEventName | | ! * pchEventName )
return ;
// see if this event matches the prefix for an achievement component
FOR_EACH_VEC ( m_vecComponentListeners [ nUserSlot ] , iAchievement )
{
CBaseAchievement * pAchievement = m_vecComponentListeners [ nUserSlot ] [ iAchievement ] ;
Assert ( pAchievement - > m_pszComponentPrefix ) ;
if ( 0 = = Q_strncmp ( pchEventName , pAchievement - > m_pszComponentPrefix , pAchievement - > m_iComponentPrefixLen ) )
{
// prefix matches, tell the achievement a component was found
pAchievement - > OnComponentEvent ( pchEventName ) ;
return ;
}
}
// look through all the map event listeners
FOR_EACH_VEC ( m_vecMapEventListeners [ nUserSlot ] , iAchievement )
{
CBaseAchievement * pAchievement = m_vecMapEventListeners [ nUserSlot ] [ iAchievement ] ;
pAchievement - > OnMapEvent ( pchEventName ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns an achievement as it's abstract object. This interface is used by gameui.dll for getting achievement info.
// Input : index -
// Output : IBaseAchievement*
//-----------------------------------------------------------------------------
IAchievement * CAchievementMgr : : GetAchievementByIndex ( int index , int nUserSlot )
{
Assert ( m_vecAchievement [ nUserSlot ] . IsValidIndex ( index ) ) ;
return ( IAchievement * ) m_vecAchievement [ nUserSlot ] [ index ] ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns an achievement as it's abstract object. This interface is used by gameui.dll for getting achievement info.
// Input : orderIndex -
// Output : IBaseAchievement*
//-----------------------------------------------------------------------------
IAchievement * CAchievementMgr : : GetAchievementByDisplayOrder ( int orderIndex , int nUserSlot )
{
Assert ( m_vecAchievementInOrder [ nUserSlot ] . IsValidIndex ( orderIndex ) ) ;
return ( IAchievement * ) m_vecAchievementInOrder [ nUserSlot ] [ orderIndex ] ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns total achievement count. This interface is used by gameui.dll for getting achievement info.
// Input : -
// Output : Count of achievements in manager's vector.
//-----------------------------------------------------------------------------
int CAchievementMgr : : GetAchievementCount ( )
{
return m_vecAchievement [ SINGLE_PLAYER_SLOT ] . Count ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Handles events from the matchmaking framework.
//-----------------------------------------------------------------------------
void CAchievementMgr : : OnEvent ( KeyValues * pEvent )
{
char const * szEvent = pEvent - > GetName ( ) ;
if ( FStrEq ( szEvent , " OnProfileDataLoaded " ) )
{
// This event is sent when the title data blocks have been loaded.
int iController = pEvent - > GetInt ( " iController " ) ;
# ifdef _X360
int nSlot = XBX_GetSlotByUserId ( iController ) ;
# else
int nSlot = STEAM_PLAYER_SLOT ;
# endif
ReadAchievementsFromTitleData ( iController , nSlot ) ;
}
# ifdef _X360
else if ( FStrEq ( szEvent , " OnProfilesChanged " ) )
{
// This is essentially a RESET
for ( int i = 0 ; i < MAX_SPLITSCREEN_PLAYERS ; + + i )
{
UserDisconnected ( i ) ;
}
// Mark the valid users as connected and try to download achievement data from LIVE
for ( unsigned int i = 0 ; i < XBX_GetNumGameUsers ( ) ; + + i )
{
UserConnected ( i ) ;
}
}
# endif
}
# if !defined(NO_STEAM)
//-----------------------------------------------------------------------------
// Purpose: called when stat download is complete
//-----------------------------------------------------------------------------
void CAchievementMgr : : Steam_OnUserStatsReceived ( UserStatsReceived_t * pUserStatsReceived )
{
# ifdef CLIENT_DLL
Assert ( steamapicontext - > SteamUserStats ( ) ) ;
if ( ! steamapicontext - > SteamUserStats ( ) )
return ;
if ( pUserStatsReceived - > m_eResult ! = k_EResultOK )
{
// DevMsg( "CAchievementMgr: failed to download stats from Steam, EResult %d\n", pUserStatsReceived->m_eResult );
return ;
}
// run through the achievements and set their achieved state according to Steam data
for ( int i = 0 ; i < m_vecAchievement [ STEAM_PLAYER_SLOT ] . Count ( ) ; + + i )
{
CBaseAchievement * pAchievement = m_vecAchievement [ STEAM_PLAYER_SLOT ] [ i ] ;
# ifndef INFESTED_DLL
char szFieldName [ 64 ] ;
# endif
int nCount = 0 ;
int nComponentBits = 0 ;
bool bAchieved = false ;
bool bRet1 = steamapicontext - > SteamUserStats ( ) - > GetAchievement ( pAchievement - > GetName ( ) , & bAchieved ) ;
# ifdef INFESTED_DLL
if ( bRet1 )
# else
// TODO: these look hardcoded for L4D2 - remove?
Q_snprintf ( szFieldName , sizeof ( szFieldName ) , " TD2.Achievements.Count.%02d " , i ) ;
bool bRet2 = steamapicontext - > SteamUserStats ( ) - > GetStat ( szFieldName , & nCount ) ;
Q_snprintf ( szFieldName , sizeof ( szFieldName ) , " TD2.Achievements.Comp.%02d " , i ) ;
bool bRet3 = steamapicontext - > SteamUserStats ( ) - > GetStat ( szFieldName , & nComponentBits ) ;
if ( bRet1 & & bRet2 & & bRet3 )
# endif
{
// set local achievement state
pAchievement - > SetAchieved ( bAchieved ) ;
pAchievement - > SetCount ( nCount ) ;
if ( pAchievement - > HasComponents ( ) )
{
pAchievement - > SetComponentBits ( nComponentBits ) ;
}
}
else
{
DevMsg ( " ISteamUserStats::GetAchievement failed for %s \n " , pAchievement - > GetName ( ) ) ;
}
pAchievement - > EvaluateIsAlreadyAchieved ( ) ;
if ( pAchievement - > StoreProgressInSteam ( ) )
{
int iValue ;
char pszProgressName [ 1024 ] ;
Q_snprintf ( pszProgressName , 1024 , " %s_STAT " , pAchievement - > GetName ( ) ) ;
bool bRet = steamapicontext - > SteamUserStats ( ) - > GetStat ( pszProgressName , & iValue ) ;
if ( bRet )
{
pAchievement - > SetCount ( iValue ) ;
}
else
{
DevMsg ( " ISteamUserStats::GetStat failed to get progress value from Steam for achievement %s \n " , pszProgressName ) ;
}
if ( pAchievement - > HasComponents ( ) )
{
Q_snprintf ( pszProgressName , 1024 , " %s_COMP " , pAchievement - > GetName ( ) ) ;
bool bRet = steamapicontext - > SteamUserStats ( ) - > GetStat ( pszProgressName , & iValue ) ;
if ( bRet )
{
pAchievement - > SetComponentBits ( iValue ) ;
}
else
{
DevMsg ( " ISteamUserStats::GetStat failed to get component value from Steam for achievement %s \n " , pszProgressName ) ;
}
}
}
}
// send an event to anyone else who needs Steam user stat data
IGameEvent * event = gameeventmanager - > CreateEvent ( " user_data_downloaded " ) ;
if ( event )
{
# ifdef GAME_DLL
gameeventmanager - > FireEvent ( event ) ;
# else
gameeventmanager - > FireEventClientSide ( event ) ;
# endif
}
# endif // CLIENT_DLL
}
//-----------------------------------------------------------------------------
// Purpose: called when stat upload is complete
//-----------------------------------------------------------------------------
void CAchievementMgr : : Steam_OnUserStatsStored ( UserStatsStored_t * pUserStatsStored )
{
if ( k_EResultOK ! = pUserStatsStored - > m_eResult )
{
DevMsg ( " CAchievementMgr: failed to upload stats to Steam, EResult %d \n " , pUserStatsStored - > m_eResult ) ;
}
else
{
if ( m_AchievementsAwarded [ STEAM_PLAYER_SLOT ] . Count ( ) > 0 )
{
# ifndef GAME_DLL
// send a message to the server about our achievement
if ( g_pGameRules & & g_pGameRules - > IsMultiplayer ( ) )
{
C_BasePlayer * pLocalPlayer = C_BasePlayer : : GetLocalPlayer ( FirstValidSplitScreenSlot ( ) ) ;
if ( pLocalPlayer )
{
char cmd [ 256 ] ;
int iPlayerID = pLocalPlayer - > GetUserID ( ) ;
unsigned short mask = UTIL_GetAchievementEventMask ( ) ;
Q_snprintf ( cmd , sizeof ( cmd ) , " achievement_earned %d %d " , m_AchievementsAwarded [ STEAM_PLAYER_SLOT ] [ 0 ] ^ mask , ( iPlayerID ^ m_AchievementsAwarded [ STEAM_PLAYER_SLOT ] [ 0 ] ) ^ mask ) ;
engine - > ClientCmd_Unrestricted ( cmd ) ;
}
}
# endif
m_AchievementsAwarded [ STEAM_PLAYER_SLOT ] . Remove ( 0 ) ;
}
// for each achievement that has not been achieved
FOR_EACH_MAP ( m_mapAchievement [ STEAM_PLAYER_SLOT ] , iAchievement )
{
CBaseAchievement * pAchievement = m_mapAchievement [ STEAM_PLAYER_SLOT ] [ iAchievement ] ;
if ( ! pAchievement - > IsAchieved ( ) )
{
pAchievement - > OnSteamUserStatsStored ( ) ;
}
}
}
2024-08-29 19:27:02 -04:00
m_flWaitingForStoreStatsCallback = 0.0f ;
if ( m_bCallStoreStatsAfterCallback ) // while waiting for this callback, we tried to store stats again. Re-send the request now.
{
m_bCallStoreStatsAfterCallback = false ;
UploadUserData ( STEAM_PLAYER_SLOT ) ;
}
2024-08-29 19:18:30 -04:00
}
# endif // !defined(NO_STEAM)
void CAchievementMgr : : ResetAchievement_Internal ( CBaseAchievement * pAchievement )
{
# ifdef CLIENT_DLL
Assert ( pAchievement ) ;
if ( steamapicontext - > SteamUserStats ( ) )
{
steamapicontext - > SteamUserStats ( ) - > ClearAchievement ( pAchievement - > GetName ( ) ) ;
}
pAchievement - > SetAchieved ( false ) ;
pAchievement - > SetCount ( 0 ) ;
if ( pAchievement - > HasComponents ( ) )
{
pAchievement - > SetComponentBits ( 0 ) ;
}
pAchievement - > SetProgressShown ( 0 ) ;
pAchievement - > StopListeningForAllEvents ( ) ;
if ( pAchievement - > IsActive ( ) )
{
pAchievement - > ListenForEvents ( ) ;
}
# endif // CLIENT_DLL
}
# ifdef CLIENT_DLL
void MsgFunc_AchievementEvent ( bf_read & msg )
{
int iAchievementID = ( int ) msg . ReadShort ( ) ;
CAchievementMgr * pAchievementMgr = dynamic_cast < CAchievementMgr * > ( engine - > GetAchievementMgr ( ) ) ;
if ( ! pAchievementMgr )
return ;
pAchievementMgr - > OnAchievementEvent ( iAchievementID , STEAM_PLAYER_SLOT ) ;
}
# ifdef _DEBUG
CON_COMMAND_F ( achievement_reset_all , " Clears all achievements " , FCVAR_CHEAT )
{
CAchievementMgr * pAchievementMgr = dynamic_cast < CAchievementMgr * > ( engine - > GetAchievementMgr ( ) ) ;
if ( ! pAchievementMgr )
return ;
pAchievementMgr - > ResetAchievements ( ) ;
}
CON_COMMAND_F ( achievement_reset , " <internal name> Clears specified achievement " , FCVAR_CHEAT )
{
CAchievementMgr * pAchievementMgr = dynamic_cast < CAchievementMgr * > ( engine - > GetAchievementMgr ( ) ) ;
if ( ! pAchievementMgr )
return ;
if ( 2 ! = args . ArgC ( ) )
{
Msg ( " Usage: achievement_reset <internal name> \n " ) ;
return ;
}
CBaseAchievement * pAchievement = pAchievementMgr - > GetAchievementByName ( args [ 1 ] , STEAM_PLAYER_SLOT ) ;
if ( ! pAchievement )
{
Msg ( " Achievement %s not found \n " , args [ 1 ] ) ;
return ;
}
pAchievementMgr - > ResetAchievement ( pAchievement - > GetAchievementID ( ) ) ;
}
CON_COMMAND_F ( achievement_status , " Shows status of all achievement " , FCVAR_CHEAT )
{
CAchievementMgr * pAchievementMgr = dynamic_cast < CAchievementMgr * > ( engine - > GetAchievementMgr ( ) ) ;
if ( ! pAchievementMgr )
return ;
pAchievementMgr - > PrintAchievementStatus ( ) ;
}
CON_COMMAND_F ( achievement_unlock , " <internal name> Unlocks achievement " , FCVAR_CHEAT )
{
CAchievementMgr * pAchievementMgr = dynamic_cast < CAchievementMgr * > ( engine - > GetAchievementMgr ( ) ) ;
if ( ! pAchievementMgr )
return ;
if ( 2 ! = args . ArgC ( ) )
{
Msg ( " Usage: achievement_unlock <internal name> \n " ) ;
return ;
}
CBaseAchievement * pAchievement = pAchievementMgr - > GetAchievementByName ( args [ 1 ] , STEAM_PLAYER_SLOT ) ;
if ( ! pAchievement )
{
Msg ( " Achievement %s not found \n " , args [ 1 ] ) ;
return ;
}
pAchievementMgr - > AwardAchievement ( pAchievement - > GetAchievementID ( ) , STEAM_PLAYER_SLOT ) ;
}
CON_COMMAND_F ( achievement_unlock_all , " Unlocks all achievements " , FCVAR_CHEAT )
{
CAchievementMgr * pAchievementMgr = dynamic_cast < CAchievementMgr * > ( engine - > GetAchievementMgr ( ) ) ;
if ( ! pAchievementMgr )
return ;
int iCount = pAchievementMgr - > GetAchievementCount ( ) ;
for ( int i = 0 ; i < iCount ; i + + )
{
IAchievement * pAchievement = pAchievementMgr - > GetAchievementByIndex ( i , STEAM_PLAYER_SLOT ) ;
if ( ! pAchievement - > IsAchieved ( ) )
{
pAchievementMgr - > AwardAchievement ( pAchievement - > GetAchievementID ( ) , STEAM_PLAYER_SLOT ) ;
}
}
}
CON_COMMAND_F ( achievement_evaluate , " <internal name> Causes failable achievement to be evaluated " , FCVAR_CHEAT )
{
CAchievementMgr * pAchievementMgr = dynamic_cast < CAchievementMgr * > ( engine - > GetAchievementMgr ( ) ) ;
if ( ! pAchievementMgr )
return ;
if ( 2 ! = args . ArgC ( ) )
{
Msg ( " Usage: achievement_evaluate <internal name> \n " ) ;
return ;
}
CBaseAchievement * pAchievement = pAchievementMgr - > GetAchievementByName ( args [ 1 ] , STEAM_PLAYER_SLOT ) ;
if ( ! pAchievement )
{
Msg ( " Achievement %s not found \n " , args [ 1 ] ) ;
return ;
}
CFailableAchievement * pFailableAchievement = dynamic_cast < CFailableAchievement * > ( pAchievement ) ;
Assert ( pFailableAchievement ) ;
if ( pFailableAchievement )
{
pFailableAchievement - > OnEvaluationEvent ( ) ;
}
}
CON_COMMAND_F ( achievement_test_friend_count , " Counts the # of teammates on local player's friends list " , FCVAR_CHEAT )
{
CAchievementMgr * pAchievementMgr = dynamic_cast < CAchievementMgr * > ( engine - > GetAchievementMgr ( ) ) ;
if ( ! pAchievementMgr )
return ;
if ( 2 ! = args . ArgC ( ) )
{
Msg ( " Usage: achievement_test_friend_count <min # of teammates> \n " ) ;
return ;
}
int iMinFriends = atoi ( args [ 1 ] ) ;
bool bRet = CalcPlayersOnFriendsList ( iMinFriends ) ;
Msg ( " You %s have at least %d friends in the game. \n " , bRet ? " do " : " do not " , iMinFriends ) ;
}
CON_COMMAND_F ( achievement_test_clan_count , " Determines if specified # of teammates belong to same clan w/local player " , FCVAR_CHEAT )
{
CAchievementMgr * pAchievementMgr = dynamic_cast < CAchievementMgr * > ( engine - > GetAchievementMgr ( ) ) ;
if ( ! pAchievementMgr )
return ;
if ( 2 ! = args . ArgC ( ) )
{
Msg ( " Usage: achievement_test_clan_count <# of clan teammates> \n " ) ;
return ;
}
int iClanPlayers = atoi ( args [ 1 ] ) ;
bool bRet = CalcHasNumClanPlayers ( iClanPlayers ) ;
Msg ( " There %s %d players who you're in a Steam group with. \n " , bRet ? " are " : " are not " , iClanPlayers ) ;
}
CON_COMMAND_F ( achievement_mark_dirty , " Mark achievement data as dirty " , FCVAR_CHEAT )
{
CAchievementMgr * pAchievementMgr = dynamic_cast < CAchievementMgr * > ( engine - > GetAchievementMgr ( ) ) ;
if ( ! pAchievementMgr )
return ;
pAchievementMgr - > SetDirty ( true , STEAM_PLAYER_SLOT ) ;
}
# endif // _DEBUG
# endif // CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: helper function to get entity model name
//-----------------------------------------------------------------------------
const char * GetModelName ( CBaseEntity * pBaseEntity )
{
CBaseAnimating * pBaseAnimating = dynamic_cast < CBaseAnimating * > ( pBaseEntity ) ;
if ( pBaseAnimating )
{
CStudioHdr * pStudioHdr = pBaseAnimating - > GetModelPtr ( ) ;
if ( pStudioHdr )
{
return pStudioHdr - > pszName ( ) ;
}
}
return " " ;
}
//-----------------------------------------------------------------------------
// Purpose: Gets the list of achievements achieved during the current game
//-----------------------------------------------------------------------------
const CUtlVector < int > & CAchievementMgr : : GetAchievedDuringCurrentGame ( int nPlayerSlot )
{
return m_AchievementsAwardedDuringCurrentGame [ nPlayerSlot ] ;
}
//-----------------------------------------------------------------------------
// Purpose: Reset the list of achievements achieved during the current game
//-----------------------------------------------------------------------------
void CAchievementMgr : : ResetAchievedDuringCurrentGame ( int nPlayerSlot )
{
m_AchievementsAwardedDuringCurrentGame [ nPlayerSlot ] . RemoveAll ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Clears achievement data for the a particular user slot
//-----------------------------------------------------------------------------
void CAchievementMgr : : ClearAchievementData ( int nUserSlot )
{
FOR_EACH_VEC ( m_vecAchievement [ nUserSlot ] , i )
{
m_mapAchievement [ nUserSlot ] [ i ] - > ClearAchievementData ( ) ;
}
}