Counter Strike : Global Offensive Source Code
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.

2528 lines
77 KiB

  1. //====== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #ifdef _WIN32
  8. #include "winerror.h"
  9. #endif
  10. #include "achievementmgr.h"
  11. #include "icommandline.h"
  12. #include "keyvalues.h"
  13. #include "filesystem.h"
  14. #include "inputsystem/InputEnums.h"
  15. #include "usermessages.h"
  16. #include "fmtstr.h"
  17. #ifdef CLIENT_DLL
  18. #include "achievement_notification_panel.h"
  19. #include "c_playerresource.h"
  20. #include "c_cs_player.h"
  21. #ifdef TF_CLIENT_DLL
  22. #include "item_inventory.h"
  23. #endif //TF_CLIENT_DLL
  24. #else
  25. #include "enginecallback.h"
  26. #endif // CLIENT_DLL
  27. #ifndef NO_STEAM
  28. #include "steam/isteamuserstats.h"
  29. #include "steam/isteamfriends.h"
  30. #include "steam/isteamutils.h"
  31. #endif
  32. #include "cs_gamerules.h"
  33. #include "tier3/tier3.h"
  34. #include "vgui/ILocalize.h"
  35. #include "matchmaking/mm_helpers.h"
  36. #ifdef _X360
  37. #include "xbox/xbox_win32stubs.h"
  38. #endif
  39. #ifdef _PS3
  40. #include "ps3/ps3_core.h"
  41. #include "ps3/ps3_win32stubs.h"
  42. #endif
  43. #ifdef _GAMECONSOLE
  44. #include "gameui/igameui.h"
  45. #include "ixboxsystem.h"
  46. #include "ienginevgui.h"
  47. #endif // _GAMECONSOLE
  48. #include "matchmaking/imatchframework.h"
  49. #include "tier0/vprof.h"
  50. #include "cs_weapon_parse.h"
  51. #include "achievements_cs.h"
  52. // NOTE: This has to be the last file included!
  53. #include "tier0/memdbgon.h"
  54. ConVar cc_achievement_debug("achievement_debug", "0", FCVAR_CHEAT | FCVAR_REPLICATED, "Turn on achievement debug msgs." );
  55. const char *COM_GetModDirectory();
  56. // We want to debug getting achievements in various development scenarios (including with cheats enabled). This convar allows bypassing
  57. // the normal check against the cheats when launched with -dev. We need to be very careful not to ship with this enabled on any platforms.
  58. // #if defined(_CERT)
  59. #define ALLOW_ACHIEVEMENTS_WITH_CHEATS 0
  60. // #else
  61. // #define ALLOW_ACHIEVEMENTS_WITH_CHEATS 1
  62. // #endif
  63. extern ConVar developer;
  64. #ifdef DEDICATED
  65. // Hack this for now until we get steam_api recompiling in the Steam codebase.
  66. ISteamUserStats *SteamUserStats()
  67. {
  68. return NULL;
  69. }
  70. #endif
  71. #if defined( XBX_GetPrimaryUserId )
  72. #undef XBX_GetPrimaryUserId
  73. #endif
  74. //-----------------------------------------------------------------------------
  75. // Helper Functions
  76. //-----------------------------------------------------------------------------
  77. static int AchievementIDCompare( CBaseAchievement * const *ach1, CBaseAchievement * const *ach2 )
  78. {
  79. return (*ach1)->GetAchievementID() > (*ach2)->GetAchievementID();
  80. }
  81. static int AchievementOrderCompare( CBaseAchievement * const *ach1, CBaseAchievement * const *ach2 )
  82. {
  83. return (*ach1)->GetDisplayOrder() - (*ach2)->GetDisplayOrder();
  84. }
  85. #ifdef _X360
  86. static TitleAchievementsDescription_t const * FindTitleAchievementByName( TitleAchievementsDescription_t const *pMap, char const *szName )
  87. {
  88. while ( pMap && pMap->m_szAchievementName )
  89. if ( !Q_stricmp( pMap->m_szAchievementName, szName ) )
  90. return pMap;
  91. else
  92. ++ pMap;
  93. return NULL;
  94. }
  95. static TitleAchievementsDescription_t const * FindTitleAchievementById( TitleAchievementsDescription_t const *pMap, int id )
  96. {
  97. while ( pMap && pMap->m_szAchievementName )
  98. if ( pMap->m_idAchievement == id )
  99. return pMap;
  100. else
  101. ++ pMap;
  102. return NULL;
  103. }
  104. #endif
  105. //-----------------------------------------------------------------------------
  106. // Constructor
  107. //-----------------------------------------------------------------------------
  108. CAchievementMgr::CAchievementMgr() : CAutoGameSystemPerFrame( "CAchievementMgr" )
  109. #if !defined(NO_STEAM)
  110. , m_CallbackUserStatsStored( this, &CAchievementMgr::Steam_OnUserStatsStored )
  111. #endif
  112. {
  113. for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  114. {
  115. SetDefLessFunc( m_mapAchievement[i] );
  116. m_flLastClassChangeTime[i] = 0;
  117. m_flTeamplayStartTime[i] = 0;
  118. m_iMiniroundsCompleted[i] = 0;
  119. m_bDirty[i] = false;
  120. m_AchievementsAwarded[i].RemoveAll();
  121. m_AchievementsAwardedDuringCurrentGame[i].RemoveAll();
  122. m_bUserSlotActive[i] = false;
  123. }
  124. m_szMap[0] = 0;
  125. m_bCheatsEverOn = false;
  126. m_flTimeLastUpload = 0;
  127. m_bCheckSigninState = true;
  128. m_bReadingFromTitleData = false;
  129. #ifdef _X360
  130. // Mark that we're not waiting for an async call to finish
  131. m_pendingAchievementState.Purge();
  132. #endif // _X360
  133. }
  134. //#if defined (_X360)
  135. void CAchievementMgr::ResetProfileInfo()
  136. {
  137. m_UIProfileInfo.m_BotDifficulty = 0;
  138. m_UIProfileInfo.m_GameMode = 0;
  139. m_UIProfileInfo.m_IsPublic = true;
  140. m_UIProfileInfo.m_Map = 0;
  141. m_UIProfileInfo.m_LeaderboardFilter = 0;
  142. m_UIProfileInfo.m_LeaderboardMode = 0;
  143. m_UIProfileInfo.m_LeaderboardType = 0;
  144. }
  145. //#endif
  146. //-----------------------------------------------------------------------------
  147. // Purpose: Initializer
  148. //-----------------------------------------------------------------------------
  149. bool CAchievementMgr::Init()
  150. {
  151. // We can be created on either client (for multiplayer games) or server
  152. // (for single player), so register ourselves with the engine so UI has a uniform place
  153. // to go get the pointer to us
  154. #ifdef _DEBUG
  155. // There can be only one achievement manager instance; no one else should be registered
  156. IAchievementMgr *pAchievementMgr = engine->GetAchievementMgr();
  157. Assert( NULL == pAchievementMgr );
  158. #endif // _DEBUG
  159. // register ourselves
  160. engine->SetAchievementMgr( GetInstanceInterface() );
  161. // register for events
  162. #ifdef GAME_DLL
  163. ListenForGameEvent( "entity_killed" );
  164. ListenForGameEvent( "game_init" );
  165. #else
  166. ListenForGameEvent( "player_death" );
  167. ListenForGameEvent( "player_stats_updated" );
  168. ListenForGameEvent( "achievement_write_failed" );
  169. ListenForGameEvent( "user_data_downloaded" );
  170. for ( int hh = 0; hh < MAX_SPLITSCREEN_PLAYERS; ++hh )
  171. {
  172. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  173. m_UMCMsgAchievementEvent.Bind< CS_UM_AchievementEvent, CCSUsrMsg_AchievementEvent >( UtlMakeDelegate( MsgFunc_AchievementEvent ) );
  174. }
  175. ListenForGameEvent( "read_game_titledata" );
  176. ListenForGameEvent( "write_game_titledata" );
  177. ListenForGameEvent( "reset_game_titledata" );
  178. ListenForGameEvent( "write_profile_data" );
  179. #endif // CLIENT_DLL
  180. #ifdef TF_CLIENT_DLL
  181. ListenForGameEvent( "localplayer_changeclass" );
  182. ListenForGameEvent( "localplayer_changeteam" );
  183. ListenForGameEvent( "teamplay_round_start" );
  184. ListenForGameEvent( "teamplay_round_win" );
  185. #endif // TF_CLIENT_DLL
  186. g_pMatchFramework->GetEventsSubscription()->Subscribe( this );
  187. return true;
  188. }
  189. //-----------------------------------------------------------------------------
  190. // Purpose: Called at init time after all systems are init'd. We have to
  191. // do this in PostInit because the Steam app ID is not available earlier.
  192. //-----------------------------------------------------------------------------
  193. void CAchievementMgr::PostInit()
  194. {
  195. // get current game dir
  196. const char *pGameDir = COM_GetModDirectory();
  197. CBaseAchievementHelper *pAchievementHelper = CBaseAchievementHelper::s_pFirst;
  198. while ( pAchievementHelper )
  199. {
  200. for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  201. {
  202. // create and initialize all achievements and insert them in our maps
  203. CBaseAchievement *pAchievement = pAchievementHelper->m_pfnCreate();
  204. pAchievement->m_pAchievementMgr = this;
  205. pAchievement->Init();
  206. pAchievement->CalcProgressMsgIncrement();
  207. pAchievement->SetUserSlot( i );
  208. // only add an achievement if it does not have a game filter (only compiled into the game it
  209. // applies to, or truly cross-game) or, if it does have a game filter, the filter matches current game.
  210. // (e.g. EP 1/2/... achievements are in shared binary but are game specific, they have a game filter for runtime check.)
  211. const char *pGameDirFilter = pAchievement->m_pGameDirFilter;
  212. if ( !pGameDirFilter || ( 0 == Q_strcmp( pGameDir, pGameDirFilter ) ) )
  213. {
  214. if ( IsX360() || ( ( IsPC() || IsPS3() ) && !pAchievement->IsAssetAward() ) )
  215. {
  216. // We don't insert achievements with asset awards on the PC.
  217. m_mapAchievement[i].Insert( pAchievement->GetAchievementID(), pAchievement );
  218. }
  219. else
  220. {
  221. delete pAchievement;
  222. }
  223. }
  224. else
  225. {
  226. // achievement is not for this game, don't use it
  227. delete pAchievement;
  228. }
  229. }
  230. pAchievementHelper = pAchievementHelper->m_pNext;
  231. }
  232. for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  233. {
  234. // Add each of the achievements to a CUtlVector ordered by achievement ID
  235. FOR_EACH_MAP( m_mapAchievement[i], iter )
  236. {
  237. if ( !m_mapAchievement[i][iter]->IsAssetAward() )
  238. {
  239. m_vecAchievement[i].AddToTail( m_mapAchievement[i][iter] );
  240. }
  241. else
  242. {
  243. m_vecAward[i].AddToTail( m_mapAchievement[i][iter] );
  244. }
  245. }
  246. m_vecAchievement[i].Sort( AchievementIDCompare );
  247. m_vecAchievementInOrder[i].AddVectorToTail( m_vecAchievement[i] );
  248. m_vecAchievementInOrder[i].Sort( AchievementOrderCompare );
  249. m_vecAwardInOrder[i].AddVectorToTail( m_vecAward[i] );
  250. m_vecAwardInOrder[i].Sort( AchievementOrderCompare );
  251. // Clear the progress and achieved data for each splitscreen player
  252. ClearAchievementData( i );
  253. }
  254. if ( IsPC() )
  255. {
  256. UserConnected( STEAM_PLAYER_SLOT );
  257. }
  258. }
  259. //-----------------------------------------------------------------------------
  260. // Purpose: Shuts down the achievement manager.
  261. //-----------------------------------------------------------------------------
  262. void CAchievementMgr::Shutdown()
  263. {
  264. SaveGlobalState();
  265. for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  266. {
  267. FOR_EACH_MAP( m_mapAchievement[i], iter )
  268. {
  269. delete m_mapAchievement[i][iter];
  270. }
  271. m_mapAchievement[i].RemoveAll();
  272. m_vecAchievement[i].RemoveAll();
  273. m_vecAward[i].RemoveAll();
  274. m_vecAchievementInOrder[i].RemoveAll();
  275. m_vecAwardInOrder[i].RemoveAll();
  276. m_vecKillEventListeners[i].RemoveAll();
  277. m_vecMapEventListeners[i].RemoveAll();
  278. m_vecComponentListeners[i].RemoveAll();
  279. m_AchievementsAwarded[i].RemoveAll();
  280. m_AchievementsAwardedDuringCurrentGame[i].RemoveAll();
  281. }
  282. g_pMatchFramework->GetEventsSubscription()->Unsubscribe( this );
  283. }
  284. //-----------------------------------------------------------------------------
  285. // Purpose: Cleans up all achievements and then re-initializes them
  286. //-----------------------------------------------------------------------------
  287. void CAchievementMgr::InitializeAchievements( )
  288. {
  289. Shutdown();
  290. PostInit();
  291. }
  292. #ifdef CLIENT_DLL
  293. extern const ConVar *sv_cheats;
  294. #endif
  295. //-----------------------------------------------------------------------------
  296. // Purpose:
  297. //-----------------------------------------------------------------------------
  298. void CAchievementMgr::SetAchievementThink( CBaseAchievement *pAchievement, float flThinkTime )
  299. {
  300. // Is the achievement already in the think list?
  301. int iCount = m_vecThinkListeners.Count();
  302. for ( int i = 0; i < iCount; i++ )
  303. {
  304. if ( m_vecThinkListeners[i].pAchievement == pAchievement )
  305. {
  306. if ( flThinkTime == THINK_CLEAR )
  307. {
  308. m_vecThinkListeners.Remove(i);
  309. return;
  310. }
  311. m_vecThinkListeners[i].m_flThinkTime = gpGlobals->curtime + flThinkTime;
  312. return;
  313. }
  314. }
  315. if ( flThinkTime == THINK_CLEAR )
  316. return;
  317. // Otherwise, add it to the list
  318. int iIdx = m_vecThinkListeners.AddToTail();
  319. m_vecThinkListeners[iIdx].pAchievement = pAchievement;
  320. m_vecThinkListeners[iIdx].m_flThinkTime = gpGlobals->curtime + flThinkTime;
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Purpose: called on level init
  324. //-----------------------------------------------------------------------------
  325. void CAchievementMgr::LevelInitPreEntity()
  326. {
  327. m_bCheatsEverOn = false;
  328. // sb: need to make sure we enable achievement manager on the client in split screen??
  329. #if defined( PORTAL2 )
  330. // portal 2 can run in both single player and multiplayer modes
  331. // achievement manager is on the client
  332. #else
  333. # ifdef GAME_DLL
  334. // For single-player games, achievement mgr must live on the server. (Only the server has detailed knowledge of game state.)
  335. Assert( !GameRules()->IsMultiplayer() );
  336. # else
  337. // For multiplayer games, achievement mgr must live on the client. (Only the client can read/write player state from Steam/XBox Live.)
  338. Assert( GameRules()->IsMultiplayer() );
  339. # endif
  340. #endif
  341. for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  342. {
  343. // clear list of achievements listening for events
  344. m_vecKillEventListeners[i].RemoveAll();
  345. m_vecMapEventListeners[i].RemoveAll();
  346. m_vecComponentListeners[i].RemoveAll();
  347. m_AchievementsAwarded[i].RemoveAll();
  348. m_flLastClassChangeTime[i] = 0;
  349. m_flTeamplayStartTime[i] = 0;
  350. m_iMiniroundsCompleted[i] = 0;
  351. }
  352. // client and server have map names available in different forms (full path on client, just file base name on server),
  353. // cache it in base file name form here so we don't have to have different code paths each time we access it
  354. #ifdef CLIENT_DLL
  355. Q_FileBase( engine->GetLevelName(), m_szMap, ARRAYSIZE( m_szMap ) );
  356. #else
  357. Q_strncpy( m_szMap, gpGlobals->mapname.ToCStr(), ARRAYSIZE( m_szMap ) );
  358. #endif // CLIENT_DLL
  359. if ( PLATFORM_EXT[0] )
  360. {
  361. // need to remove the .360 extension on the end of the map name
  362. char *pExt = Q_stristr( m_szMap, PLATFORM_EXT );
  363. if ( pExt )
  364. {
  365. *pExt = '\0';
  366. }
  367. }
  368. for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  369. {
  370. // Don't bother listening if a slot is not active
  371. if ( m_bUserSlotActive[i] )
  372. {
  373. // look through all achievements, see which ones we want to have listen for events
  374. FOR_EACH_MAP( m_mapAchievement[i], iAchievement )
  375. {
  376. CBaseAchievement *pAchievement = m_mapAchievement[i][iAchievement];
  377. // if the achievement only applies to a specific map, and it's not the current map, skip it
  378. const char *pMapNameFilter = pAchievement->m_pMapNameFilter;
  379. if ( pMapNameFilter && ( 0 != Q_strcmp( m_szMap, pMapNameFilter ) ) )
  380. continue;
  381. // if the achievement needs kill events, add it as a listener
  382. if ( pAchievement->GetFlags() & ACH_LISTEN_KILL_EVENTS )
  383. {
  384. m_vecKillEventListeners[i].AddToTail( pAchievement );
  385. }
  386. // if the achievement needs map events, add it as a listener
  387. if ( pAchievement->GetFlags() & ACH_LISTEN_MAP_EVENTS )
  388. {
  389. m_vecMapEventListeners[i].AddToTail( pAchievement );
  390. }
  391. // if the achievement needs map events, add it as a listener
  392. if ( pAchievement->GetFlags() & ACH_LISTEN_COMPONENT_EVENTS )
  393. {
  394. m_vecComponentListeners[i].AddToTail( pAchievement );
  395. }
  396. if ( pAchievement->IsActive() )
  397. {
  398. pAchievement->ListenForEvents();
  399. }
  400. }
  401. m_flLevelInitTime[i] = gpGlobals->curtime;
  402. }
  403. }
  404. }
  405. //-----------------------------------------------------------------------------
  406. // Purpose: called on level shutdown
  407. //-----------------------------------------------------------------------------
  408. void CAchievementMgr::LevelShutdownPreEntity()
  409. {
  410. for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  411. {
  412. // make all achievements stop listening for events
  413. FOR_EACH_MAP( m_mapAchievement[i], iAchievement )
  414. {
  415. CBaseAchievement *pAchievement = m_mapAchievement[i][iAchievement];
  416. pAchievement->StopListeningForAllEvents();
  417. }
  418. }
  419. // save global state if we have any changes
  420. SaveGlobalStateIfDirty();
  421. // $FIXME(hpe) guessing this is a hack we care to fix
  422. // 12/2/2008ywb: HACK, this needs to know the nUserSlot!!!
  423. int nUserSlot = 0;
  424. UploadUserData( nUserSlot );
  425. }
  426. //$FIXME(hpe) we should probably guard for out of index nUserSlot values in all of these getachievement functions
  427. //-----------------------------------------------------------------------------
  428. // Purpose: returns achievement for specified ID
  429. //-----------------------------------------------------------------------------
  430. CBaseAchievement *CAchievementMgr::GetAchievementByID( int iAchievementID, int nUserSlot )
  431. {
  432. Assert(nUserSlot < MAX_SPLITSCREEN_PLAYERS);
  433. if( nUserSlot >= MAX_SPLITSCREEN_PLAYERS )
  434. {
  435. return NULL;
  436. }
  437. int iAchievement = m_mapAchievement[nUserSlot].Find( iAchievementID );
  438. if ( iAchievement != m_mapAchievement[nUserSlot].InvalidIndex() )
  439. {
  440. return m_mapAchievement[nUserSlot][iAchievement];
  441. }
  442. return NULL;
  443. }
  444. //-----------------------------------------------------------------------------
  445. // Purpose: returns achievement with specified name. NOTE: this iterates through
  446. // all achievements to find the name, intended for debugging purposes.
  447. // Use GetAchievementByID for fast lookup.
  448. //-----------------------------------------------------------------------------
  449. CBaseAchievement *CAchievementMgr::GetAchievementByName( const char *pchName, int nUserSlot )
  450. {
  451. VPROF("GetAchievementByName");
  452. Assert(nUserSlot < MAX_SPLITSCREEN_PLAYERS);
  453. if( nUserSlot >= MAX_SPLITSCREEN_PLAYERS )
  454. {
  455. return NULL;
  456. }
  457. FOR_EACH_MAP_FAST( m_mapAchievement[nUserSlot], i )
  458. {
  459. CBaseAchievement *pAchievement = m_mapAchievement[nUserSlot][i];
  460. if ( pAchievement && 0 == ( Q_stricmp( pchName, pAchievement->GetName() ) ) )
  461. return pAchievement;
  462. }
  463. return NULL;
  464. }
  465. //-----------------------------------------------------------------------------
  466. // Purpose: Returns true if the achievement with the specified name has been achieved
  467. //-----------------------------------------------------------------------------
  468. bool CAchievementMgr::HasAchieved( const char *pchName, int nUserSlot )
  469. {
  470. CBaseAchievement *pAchievement = GetAchievementByName( pchName, nUserSlot );
  471. if ( pAchievement )
  472. return pAchievement->IsAchieved();
  473. return false;
  474. }
  475. //-----------------------------------------------------------------------------
  476. // Purpose: downloads user data from Steam or XBox Live
  477. //-----------------------------------------------------------------------------
  478. void CAchievementMgr::UserDisconnected( int nUserSlot )
  479. {
  480. m_bUserSlotActive[nUserSlot] = false;
  481. ClearAchievementData( nUserSlot );
  482. }
  483. //-----------------------------------------------------------------------------
  484. // Read our achievement data from the title data.
  485. //-----------------------------------------------------------------------------
  486. void CAchievementMgr::ReadAchievementsFromTitleData( int iController, int iSlot )
  487. {
  488. IPlayerLocal *pPlayer = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iController );
  489. if ( !pPlayer )
  490. return;
  491. m_bReadingFromTitleData = true;
  492. // Set the incremental achievement progress and components
  493. for ( int i=0; i<m_vecAchievement[iSlot].Count(); ++i )
  494. {
  495. CBaseAchievement *pAchievement = m_vecAchievement[iSlot][i];
  496. if ( pAchievement )
  497. {
  498. pAchievement->ReadProgress( pPlayer );
  499. }
  500. }
  501. for ( int i=0; i<m_vecAward[iSlot].Count(); ++i )
  502. {
  503. CBaseAchievement *pAward = m_vecAward[iSlot][i];
  504. if ( pAward )
  505. {
  506. pAward->ReadProgress( pPlayer );
  507. }
  508. }
  509. m_bReadingFromTitleData = false;
  510. SaveGlobalStateIfDirty();
  511. }
  512. //-----------------------------------------------------------------------------
  513. // Purpose: downloads user data from Steam or XBox Live
  514. //-----------------------------------------------------------------------------
  515. void CAchievementMgr::UserConnected( int nUserSlot )
  516. {
  517. #ifdef CLIENT_DLL
  518. if ( IsPC() || IsPS3() )
  519. {
  520. #ifdef _PS3
  521. if ( XBX_GetUserIsGuest( nUserSlot ) )
  522. return;
  523. const int iController = XBX_GetUserId( nUserSlot );
  524. if ( iController == XBX_INVALID_USER_ID )
  525. return;
  526. #endif
  527. // ASSERT( STEAM_PLAYER_SLOT == nUserSlot )
  528. m_bUserSlotActive[STEAM_PLAYER_SLOT] = true;
  529. }
  530. else if ( IsX360() )
  531. {
  532. #if defined( _X360 )
  533. if ( XBX_GetUserIsGuest( nUserSlot ) )
  534. return;
  535. const int iController = XBX_GetUserId( nUserSlot );
  536. if ( iController == XBX_INVALID_USER_ID )
  537. return;
  538. if ( XUserGetSigninState( iController ) == eXUserSigninState_NotSignedIn )
  539. return;
  540. m_bUserSlotActive[nUserSlot] = true;
  541. // Download achievements from XBox Live
  542. const DWORD nTotalAchievements = GetAchievementCount();
  543. DWORD nTotalX360AchsEnumerated = 0;
  544. HANDLE hEnumerator = NULL;
  545. DWORD bytes;
  546. DWORD ret = XUserCreateAchievementEnumerator( 0, iController, INVALID_XUID, XACHIEVEMENT_DETAILS_ALL, 0, nTotalAchievements, &bytes, &hEnumerator );
  547. if ( ret != ERROR_SUCCESS )
  548. {
  549. Warning( "Enumerate Achievements for controller %d failed! Failed to create enumerator with code %d.\n", iController, ret );
  550. return;
  551. }
  552. // Allocate the buffer
  553. CUtlVector< char > vBuffer;
  554. vBuffer.SetCount( bytes );
  555. // Enumerate the achievements from Live
  556. ret = XEnumerate( hEnumerator, vBuffer.Base(), bytes, &nTotalX360AchsEnumerated, NULL );
  557. CloseHandle( hEnumerator );
  558. hEnumerator = NULL;
  559. if ( ret != ERROR_SUCCESS )
  560. {
  561. Warning( "Enumerate Achievements for controller %d failed! Failed to enumerate with code %d.\n", iController, ret );
  562. return;
  563. }
  564. if ( nTotalX360AchsEnumerated != nTotalAchievements )
  565. {
  566. Warning( "Enumerate achievements returned %d achievements != %d total registered achievements!\n",
  567. nTotalX360AchsEnumerated, nTotalAchievements );
  568. }
  569. #if !defined ( CSTRIKE15 )
  570. // Give live a chance to mark achievements as unlocked, in case the achievement manager
  571. // wasn't able to get that data (storage device missing, read failure, etc)
  572. XACHIEVEMENT_DETAILS const *pXboxAchievements = ( XACHIEVEMENT_DETAILS const * ) vBuffer.Base();
  573. TitleAchievementsDescription_t const *pTitleAchMap = g_pMatchFramework->GetMatchTitle()->DescribeTitleAchievements();
  574. for ( DWORD i = 0; i < nTotalX360AchsEnumerated; ++i )
  575. {
  576. TitleAchievementsDescription_t const *pAchEntry = FindTitleAchievementById( pTitleAchMap, pXboxAchievements[i].dwId );
  577. if ( !pAchEntry )
  578. {
  579. Warning( "X360 downloaded title achievement ID=%d is not in title achievement map, skipping!\n", pXboxAchievements[i].dwId );
  580. continue;
  581. }
  582. CBaseAchievement *pAchievement = GetAchievementByName( pAchEntry->m_szAchievementName, nUserSlot );
  583. if ( !pAchievement )
  584. continue;
  585. // Give Live a chance to claim the achievement as unlocked
  586. if ( AchievementEarned( pXboxAchievements[i].dwFlags ) )
  587. {
  588. pAchievement->SetAchieved( true );
  589. pAchievement->CheckAssetAwards( nUserSlot );
  590. }
  591. else
  592. {
  593. pAchievement->SetAchieved( false );
  594. }
  595. }
  596. #endif
  597. #endif // X360
  598. }
  599. #endif // CLIENT_DLL
  600. }
  601. const char *COM_GetModDirectory()
  602. {
  603. static char modDir[MAX_PATH];
  604. if ( Q_strlen( modDir ) == 0 )
  605. {
  606. const char *gamedir = CommandLine()->ParmValue("-game", CommandLine()->ParmValue( "-defaultgamedir", "hl2" ) );
  607. Q_strncpy( modDir, gamedir, sizeof(modDir) );
  608. if ( strchr( modDir, '/' ) || strchr( modDir, '\\' ) )
  609. {
  610. Q_StripLastDir( modDir, sizeof(modDir) );
  611. int dirlen = Q_strlen( modDir );
  612. Q_strncpy( modDir, gamedir + dirlen, sizeof(modDir) - dirlen );
  613. }
  614. }
  615. return modDir;
  616. }
  617. //-----------------------------------------------------------------------------
  618. // Purpose: Uploads user data to Steam or XBox Live
  619. //-----------------------------------------------------------------------------
  620. void CAchievementMgr::UploadUserData( int nUserSlot )
  621. {
  622. #if defined( CLIENT_DLL ) && !defined( NO_STEAM )
  623. if ( ( IsPC() || IsPS3() ) && ( nUserSlot == STEAM_PLAYER_SLOT ) )
  624. {
  625. if ( steamapicontext->SteamUserStats() )
  626. {
  627. // Upload current Steam client achievements & stats state to Steam. Will get called back at OnUserStatsStored when complete.
  628. // Only values previously set via SteamUserStats() get uploaded
  629. steamapicontext->SteamUserStats()->StoreStats();
  630. m_flTimeLastUpload = Plat_FloatTime();
  631. }
  632. }
  633. #endif
  634. }
  635. //-----------------------------------------------------------------------------
  636. // Purpose: saves global state to file
  637. //-----------------------------------------------------------------------------
  638. void CAchievementMgr::SaveGlobalState( )
  639. {
  640. int iController = 0;
  641. #ifdef _X360
  642. for ( int j = 0; j < MAX_SPLITSCREEN_PLAYERS; ++ j )
  643. {
  644. if ( !IsUserConnected( j ) )
  645. continue;
  646. iController = XBX_GetUserId( j );
  647. #else
  648. int j = STEAM_PLAYER_SLOT;
  649. VPROF_BUDGET( "CAchievementMgr::SaveGlobalState", "Achievements" );
  650. {
  651. #endif
  652. IPlayerLocal *pPlayer = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iController );
  653. if ( !pPlayer )
  654. #ifdef _X360
  655. continue;
  656. #else
  657. return;
  658. #endif
  659. for ( int i = 0; i < m_vecAchievement[j].Count(); ++i )
  660. {
  661. CBaseAchievement *pAchievement = m_vecAchievement[j][i];
  662. if ( pAchievement )
  663. {
  664. pAchievement->WriteProgress( pPlayer );
  665. }
  666. }
  667. for ( int i = 0; i < m_vecAward[j].Count(); ++i )
  668. {
  669. CBaseAchievement *pAward = m_vecAward[j][i];
  670. if ( pAward )
  671. {
  672. pAward->WriteProgress( pPlayer );
  673. }
  674. }
  675. if ( IsPC() || IsPS3() )
  676. {
  677. m_bDirty[iController] = false;
  678. }
  679. }
  680. }
  681. void CAchievementMgr::SendResetProfileEvent()
  682. {
  683. IGameEvent * resetUserProfileEvent = gameeventmanager->CreateEvent( "reset_user_profile" );
  684. if (resetUserProfileEvent)
  685. gameeventmanager->FireEventClientSide( resetUserProfileEvent );
  686. }
  687. void CAchievementMgr::SendWriteProfileEvent()
  688. {
  689. IGameEvent * writeUserProfileEvent = gameeventmanager->CreateEvent( "write_user_profile" );
  690. if (writeUserProfileEvent)
  691. gameeventmanager->FireEventClientSide( writeUserProfileEvent );
  692. }
  693. //-----------------------------------------------------------------------------
  694. // Purpose: saves global state to file if there have been any changes
  695. //-----------------------------------------------------------------------------
  696. void CAchievementMgr::SaveGlobalStateIfDirty( )
  697. {
  698. if ( m_bDirty && !m_bReadingFromTitleData )
  699. {
  700. SaveGlobalState( );
  701. }
  702. }
  703. bool CAchievementMgr::IsAchievementAllowedInGame( int iAchievementID )
  704. {
  705. // Offline modes with trivial bots disable ALL achievements
  706. if ( CSGameRules() && !CSGameRules()->IsAwardsProgressAllowedForBotDifficulty() )
  707. return false;
  708. bool isGunGameAchievement = false;
  709. switch( iAchievementID )
  710. {
  711. // a list of gun game achievements
  712. //case CSFastRoundWin:
  713. case CSGunGameKillKnifer:
  714. case CSWinEveryGGMap:
  715. case CSGunGameProgressiveRampage:
  716. case CSGunGameFirstKill:
  717. case CSFirstBulletKills:
  718. case CSGunGameConservationist:
  719. case CSWinMatchAR_SHOOTS:
  720. case CSWinMatchAR_BAGGAGE:
  721. case CSPlantBombsTRLow:
  722. case CSDefuseBombsTRLow:
  723. case CSKillEnemyTerrTeamBeforeBombPlant:
  724. case CSKillEnemyCTTeamBeforeBombPlant:
  725. case CSWinMatchDE_LAKE:
  726. case CSWinMatchDE_SAFEHOUSE:
  727. case CSWinMatchDE_SUGARCANE:
  728. case CSWinMatchDE_STMARC:
  729. case CSWinMatchDE_BANK:
  730. case CSWinMatchDE_EMBASSY:
  731. case CSWinMatchDE_DEPOT:
  732. case CSWinMatchDE_SHORTTRAIN:
  733. //case CSHipShot:
  734. case CSBornReady:
  735. case CSSpawnCamper:
  736. case CSGunGameKnifeKillKnifer:
  737. case CSGunGameSMGKillKnifer:
  738. case CSStillAlive:
  739. case CSGGWinRoundsLow:
  740. case CSGGWinRoundsMed:
  741. case CSGGWinRoundsHigh:
  742. case CSGGWinRoundsExtreme:
  743. case CSGGWinRoundsUltimate:
  744. case CSGGRoundsLow:
  745. case CSGGRoundsMed:
  746. case CSGGRoundsHigh:
  747. case CSPlayEveryGGMap:
  748. case CSDominationsLow:
  749. case CSDominationsHigh:
  750. case CSRevengesLow:
  751. case CSRevengesHigh:
  752. case CSDominationOverkillsLow:
  753. case CSDominationOverkillsHigh:
  754. case CSDominationOverkillsMatch:
  755. case CSExtendedDomination:
  756. case CSConcurrentDominations:
  757. //case CSAvengeFriend:
  758. isGunGameAchievement = true;
  759. };
  760. // Only unlock gungame achievements in gun game modes
  761. if( isGunGameAchievement )
  762. {
  763. return CSGameRules()->IsPlayingGunGame();
  764. }
  765. else
  766. {
  767. // Other achievements are valid for all game types.
  768. return true;
  769. }
  770. }
  771. //-----------------------------------------------------------------------------
  772. // Purpose: awards specified achievement
  773. //-----------------------------------------------------------------------------
  774. void CAchievementMgr::AwardAchievement( int iAchievementID, int nUserSlot )
  775. {
  776. if( !IsAchievementAllowedInGame( iAchievementID ) )
  777. return;
  778. #ifdef CLIENT_DLL
  779. C_BasePlayer *pPlayerLocal = C_BasePlayer::GetLocalPlayer();
  780. C_CSPlayer* pPlayer = ToCSPlayer(pPlayerLocal);
  781. if( ( pPlayer && pPlayer->IsControllingBot() ) ) // if we're controlling a bot, no achievements for us!
  782. return;
  783. CBaseAchievement *pAchievement = GetAchievementByID( iAchievementID, nUserSlot );
  784. Assert( pAchievement );
  785. if ( !pAchievement )
  786. return;
  787. #if defined ( _X360 ) && !defined ( CSTRIKE15 )
  788. TitleAchievementsDescription_t const *pAchEntryMap = g_pMatchFramework->GetMatchTitle()->DescribeTitleAchievements();
  789. TitleAchievementsDescription_t const *pAchEntry = FindTitleAchievementByName( pAchEntryMap, pAchievement->GetName() );
  790. if ( !pAchEntry && !pAchievement->IsAssetAward() )
  791. {
  792. Warning( "X360 cannot award title achievement '%s' ID=%d because it is not in title achievement map, skipping!\n", pAchievement->GetName(), iAchievementID );
  793. return;
  794. }
  795. #endif
  796. if ( !CheckAchievementsEnabled() )
  797. {
  798. Msg( "Achievements disabled, ignoring achievement unlock for %s\n", pAchievement->GetName() );
  799. return;
  800. }
  801. if ( pAchievement->IsAchieved() )
  802. {
  803. if ( cc_achievement_debug.GetInt() > 0 )
  804. {
  805. Msg( "Achievement award called but already achieved: %s\n", pAchievement->GetName() );
  806. }
  807. return;
  808. }
  809. pAchievement->SetAchieved( true );
  810. pAchievement->OnAchieved();
  811. #if defined ( CSTRIKE15 )
  812. IGameEvent * event = gameeventmanager->CreateEvent( "achievement_earned_local" , true );
  813. if ( event )
  814. {
  815. event->SetInt( "achievement", pAchievement->GetAchievementID() );
  816. event->SetInt( "splitscreenplayer", nUserSlot );
  817. gameeventmanager->FireEventClientSide( event );
  818. }
  819. #if defined ( CLIENT_DLL )
  820. STEAMWORKS_TESTSECRET();
  821. #endif
  822. #endif
  823. if ( cc_achievement_debug.GetInt() > 0 )
  824. {
  825. Msg( "Achievement awarded: %s\n", pAchievement->GetName() );
  826. }
  827. // save state at next good opportunity. (Don't do it immediately, may hitch at bad time.)
  828. m_bDirty[nUserSlot] = true;
  829. if ( IsPC() || IsPS3() )
  830. {
  831. #ifndef NO_STEAM
  832. if ( steamapicontext->SteamUserStats() )
  833. {
  834. VPROF_BUDGET( "AwardAchievement", VPROF_BUDGETGROUP_STEAM );
  835. // set this achieved in the Steam client
  836. bool bRet = steamapicontext->SteamUserStats()->SetAchievement( pAchievement->GetName() );
  837. // Assert( bRet );
  838. if ( bRet )
  839. {
  840. // upload achievement to steam
  841. UploadUserData( nUserSlot );
  842. m_AchievementsAwarded[nUserSlot].AddToTail( iAchievementID );
  843. }
  844. }
  845. #endif
  846. }
  847. else if ( IsX360() )
  848. {
  849. #ifdef _X360
  850. #if !defined ( CSTRIKE15 )
  851. if ( xboxsystem )
  852. {
  853. if ( pAchievement->IsAssetAward() )
  854. {
  855. // Fire off the asynchronous asset award operation.
  856. PendingAchievementInfo_t pendingAssetAwardState = { iAchievementID, nUserSlot, NULL };
  857. xboxsystem->AwardAvatarAsset( XBX_GetUserId( nUserSlot ), pAchievement->GetAssetAwardID(), &pendingAssetAwardState.pOverlappedResult );
  858. m_pendingAchievementState.AddToTail( pendingAssetAwardState );
  859. }
  860. else
  861. {
  862. // Fire off the asynchronous achievement award operation.
  863. PendingAchievementInfo_t pendingAchievementState = { iAchievementID, nUserSlot, NULL };
  864. xboxsystem->AwardAchievement( XBX_GetUserId( nUserSlot ), pAchEntry->m_idAchievement, &pendingAchievementState.pOverlappedResult );
  865. m_pendingAchievementState.AddToTail( pendingAchievementState );
  866. }
  867. }
  868. #endif
  869. #endif
  870. }
  871. SaveGlobalStateIfDirty();
  872. // Add this one to the list of achievements earned during current session
  873. m_AchievementsAwardedDuringCurrentGame[nUserSlot].AddToTail( iAchievementID );
  874. #endif // CLIENT_DLL
  875. }
  876. #if defined ( _X360 )
  877. void CAchievementMgr::AwardXBoxAchievement( int iAchievementID, int iXBoxAchievementID, int nUserSlot )
  878. {
  879. if ( xboxsystem->IsArcadeTitleUnlocked() )
  880. {
  881. PendingAchievementInfo_t pendingAchievementState = { iAchievementID, nUserSlot, NULL };
  882. xboxsystem->AwardAchievement( XBX_GetUserId( nUserSlot ), iXBoxAchievementID, &pendingAchievementState.pOverlappedResult );
  883. // Save off the results for checking later
  884. m_pendingAchievementState.AddToTail( pendingAchievementState );
  885. }
  886. }
  887. #endif
  888. //-----------------------------------------------------------------------------
  889. // Purpose: updates specified achievement
  890. //-----------------------------------------------------------------------------
  891. void CAchievementMgr::UpdateAchievement( int iAchievementID, int nData, int nUserSlot )
  892. {
  893. CBaseAchievement *pAchievement = GetAchievementByID( iAchievementID, nUserSlot );
  894. Assert( pAchievement );
  895. if ( !pAchievement )
  896. return;
  897. if ( !CheckAchievementsEnabled() )
  898. {
  899. Msg( "Achievements disabled, ignoring achievement update for %s\n", pAchievement->GetName() );
  900. return;
  901. }
  902. if ( pAchievement->IsAchieved() )
  903. {
  904. if ( cc_achievement_debug.GetInt() > 0 )
  905. {
  906. Msg( "Achievement update called but already achieved: %s\n", pAchievement->GetName() );
  907. }
  908. return;
  909. }
  910. pAchievement->UpdateAchievement( nData );
  911. }
  912. #if defined( CLIENT_DLL ) && !defined( NO_STEAM )
  913. void CAchievementMgr::UpdateStateFromSteam_Internal( int nUserSlot )
  914. {
  915. #if !defined ( _X360 )
  916. Assert( steamapicontext->SteamUserStats() );
  917. if ( !steamapicontext->SteamUserStats() )
  918. return;
  919. // run through the achievements and set their achieved state according to Steam data
  920. FOR_EACH_MAP( m_mapAchievement[nUserSlot], i )
  921. {
  922. CBaseAchievement *pAchievement = m_mapAchievement[nUserSlot][i];
  923. bool bAchieved = false;
  924. uint32 unlockTime;
  925. // Get the achievement status, and the time it was unlocked if unlocked.
  926. // If the return value is true, but the unlock time is zero, that means it was unlocked before Steam
  927. // began tracking achievement unlock times (December 2009). Time is seconds since January 1, 1970.
  928. bool bRet = steamapicontext->SteamUserStats()->GetAchievementAndUnlockTime( pAchievement->GetName(), &bAchieved, &unlockTime );
  929. if ( bRet )
  930. {
  931. // set local achievement state
  932. pAchievement->SetAchieved( bAchieved );
  933. pAchievement->SetUnlockTime(unlockTime);
  934. }
  935. else
  936. {
  937. DevMsg( "ISteamUserStats::GetAchievement failed for %s\n", pAchievement->GetName() );
  938. }
  939. if ( pAchievement->StoreProgressInSteam() )
  940. {
  941. int iValue;
  942. char pszProgressName[1024];
  943. Q_snprintf( pszProgressName, 1024, "%s_STAT", pAchievement->GetName() );
  944. bRet = steamapicontext->SteamUserStats()->GetStat( pszProgressName, &iValue );
  945. if ( bRet )
  946. {
  947. pAchievement->SetCount( iValue );
  948. pAchievement->EvaluateNewAchievement();
  949. }
  950. else
  951. {
  952. DevMsg( "ISteamUserStats::GetStat failed to get progress value from Steam for achievement %s\n", pszProgressName );
  953. }
  954. }
  955. }
  956. IGameEvent * event = gameeventmanager->CreateEvent( "achievement_info_loaded" );
  957. if ( event )
  958. {
  959. gameeventmanager->FireEventClientSide( event );
  960. }
  961. #endif
  962. }
  963. #endif
  964. //-----------------------------------------------------------------------------
  965. // Purpose: clears state for all achievements
  966. //-----------------------------------------------------------------------------
  967. void CAchievementMgr::PreRestoreSavedGame()
  968. {
  969. for ( int j = 0; j < MAX_SPLITSCREEN_PLAYERS; ++j )
  970. {
  971. FOR_EACH_MAP( m_mapAchievement[j], i )
  972. {
  973. m_mapAchievement[j][i]->PreRestoreSavedGame();
  974. }
  975. }
  976. }
  977. //-----------------------------------------------------------------------------
  978. // Purpose: clears state for all achievements
  979. //-----------------------------------------------------------------------------
  980. void CAchievementMgr::PostRestoreSavedGame()
  981. {
  982. for ( int j = 0; j < MAX_SPLITSCREEN_PLAYERS; ++j )
  983. {
  984. FOR_EACH_MAP( m_mapAchievement[j], i )
  985. {
  986. m_mapAchievement[j][i]->PostRestoreSavedGame();
  987. }
  988. }
  989. }
  990. extern bool IsInCommentaryMode( void );
  991. ConVar cc_achievement_disable("achievement_disable", "0", FCVAR_CHEAT | FCVAR_REPLICATED, "Turn off achievements." );
  992. //-----------------------------------------------------------------------------
  993. // Purpose: checks if achievements are enabled
  994. //-----------------------------------------------------------------------------
  995. bool CAchievementMgr::CheckAchievementsEnabled( )
  996. {
  997. // if PC, Steam must be running and user logged in
  998. if ( cc_achievement_disable.GetBool() )
  999. return false;
  1000. if ( IsPC() && !LoggedIntoSteam() )
  1001. {
  1002. Msg( "Achievements disabled: Steam not running.\n" );
  1003. return false;
  1004. }
  1005. //No achievements in demo version.
  1006. #ifdef _DEMO
  1007. return false;
  1008. #endif
  1009. #if defined( _X360 ) && defined( CLIENT_DLL )
  1010. if ( m_bCheckSigninState )
  1011. {
  1012. uint state = XUserGetSigninState( XBX_GetActiveUserId() );
  1013. if ( state == eXUserSigninState_NotSignedIn )
  1014. {
  1015. Msg( "Achievements disabled: not signed in to XBox user account.\n" );
  1016. return false;
  1017. }
  1018. }
  1019. #endif
  1020. // can't be in commentary mode, user is invincible
  1021. if ( IsInCommentaryMode() )
  1022. {
  1023. Msg( "Achievements disabled: in commentary mode.\n" );
  1024. return false;
  1025. }
  1026. #ifdef CLIENT_DLL
  1027. // achievements disabled if playing demo (Playback demo) or watching HLTV
  1028. if ( engine->IsHLTV() )
  1029. {
  1030. Msg( "Achievements disabled: demo playing.\n" );
  1031. return false;
  1032. }
  1033. #endif // CLIENT_DLL
  1034. if ( IsPC() )
  1035. {
  1036. #if ALLOW_ACHIEVEMENTS_WITH_CHEATS
  1037. if ( developer.GetInt() != 0 )
  1038. return true;
  1039. #endif // ALLOW_ACHIEVEMENTS_WITH_CHEATS
  1040. // Don't award achievements if cheats are turned on.
  1041. if ( WereCheatsEverOn() )
  1042. {
  1043. // 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
  1044. // on Steam public
  1045. #if defined( CLIENT_DLL ) && !defined( NO_STEAM )
  1046. if ( ( developer.GetInt() == 0 ) || !steamapicontext->SteamUtils() || ( k_EUniversePublic == steamapicontext->SteamUtils()->GetConnectedUniverse() ) )
  1047. #else
  1048. if ( developer.GetInt() == 0 )
  1049. #endif
  1050. {
  1051. Msg( "Achievements disabled: cheats turned on in this app session.\n" );
  1052. return false;
  1053. }
  1054. }
  1055. }
  1056. return true;
  1057. }
  1058. //-----------------------------------------------------------------------------
  1059. // Purpose: Determine friendness on xbox
  1060. //-----------------------------------------------------------------------------
  1061. #if defined ( _X360 )
  1062. bool IsXboxFriends( int userID, int entityIndex )
  1063. {
  1064. // $TODO(hpe) connect the matchmaking bits
  1065. return false;
  1066. //if ( !matchmaking )
  1067. // return false;
  1068. //XUID XUid[1];
  1069. //XUid[0] = matchmaking->PlayerIdToXuid( entityIndex );
  1070. //BOOL bFriend = false;
  1071. //// If we don't have a XUID, we don't even need to bother asking...
  1072. //if ( XUid[0] == 0 )
  1073. //{
  1074. // return false;
  1075. //}
  1076. //XUserAreUsersFriends( userID, XUid, 1, &bFriend, NULL );
  1077. //return bFriend;
  1078. }
  1079. #endif
  1080. #ifdef CLIENT_DLL
  1081. //-----------------------------------------------------------------------------
  1082. // Purpose: Returns the whether all of the local player's team mates are
  1083. // on her friends list, and if there are at least the specified # of
  1084. // teammates. Involves cross-process calls to Steam so this is mildly
  1085. // expensive, don't call this every frame.
  1086. //-----------------------------------------------------------------------------
  1087. bool CalcPlayersOnFriendsList( int iMinFriends )
  1088. {
  1089. // Got message during connection
  1090. if ( !g_PR )
  1091. return false;
  1092. Assert( g_pGameRules->IsMultiplayer() );
  1093. // Do a cheap rejection: check teammate count first to see if we even need to bother checking w/Steam
  1094. // Subtract 1 for the local player.
  1095. if ( CalcPlayerCount()-1 < iMinFriends )
  1096. return false;
  1097. // determine local player team
  1098. int iLocalPlayerIndex = GetLocalPlayerIndex();
  1099. uint64 XPlayerUid = 0;
  1100. if ( IsPC() || IsPS3() )
  1101. {
  1102. #ifndef NO_STEAM
  1103. if ( !steamapicontext->SteamFriends() || !steamapicontext->SteamUtils() || !g_pGameRules->IsMultiplayer() )
  1104. #endif
  1105. return false;
  1106. }
  1107. else if ( IsX360() )
  1108. {
  1109. if ( !g_pMatchFramework )
  1110. return false;
  1111. XPlayerUid = XBX_GetActiveUserId();
  1112. }
  1113. else
  1114. {
  1115. // other platforms...?
  1116. return false;
  1117. }
  1118. // Loop through the players
  1119. int iTotalFriends = 0;
  1120. for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
  1121. {
  1122. // find all players who are on the local player's team
  1123. if( ( iPlayerIndex != iLocalPlayerIndex ) && ( g_PR->IsConnected( iPlayerIndex ) ) )
  1124. {
  1125. player_info_t pi;
  1126. if ( !engine->GetPlayerInfo( iPlayerIndex, &pi ) )
  1127. continue;
  1128. if ( !pi.xuid )
  1129. continue;
  1130. #ifdef _X360
  1131. // check and see if they're on the local player's friends list
  1132. BOOL bFriend = FALSE;
  1133. XUserAreUsersFriends( XPlayerUid, &pi.xuid, 1, &bFriend, NULL );
  1134. if ( !bFriend )
  1135. continue;
  1136. #elif !defined( NO_STEAM )
  1137. // check and see if they're on the local player's friends list
  1138. if ( !steamapicontext->SteamFriends()->HasFriend( pi.xuid, /*k_EFriendFlagImmediate*/ 0x04 ) )
  1139. continue;
  1140. #else
  1141. continue;
  1142. #endif
  1143. iTotalFriends++;
  1144. }
  1145. }
  1146. return (iTotalFriends >= iMinFriends);
  1147. }
  1148. //-----------------------------------------------------------------------------
  1149. // Purpose: Returns whether there are a specified # of teammates who all belong
  1150. // to same clan as local player. Involves cross-process calls to Steam
  1151. // so this is mildly expensive, don't call this every frame.
  1152. //-----------------------------------------------------------------------------
  1153. bool CalcHasNumClanPlayers( int iClanTeammates )
  1154. {
  1155. Assert( g_pGameRules->IsMultiplayer() );
  1156. if ( IsPC() || IsPS3() )
  1157. {
  1158. #ifndef NO_STEAM
  1159. // Do a cheap rejection: check teammate count first to see if we even need to bother checking w/Steam
  1160. // Subtract 1 for the local player.
  1161. if ( CalcPlayerCount()-1 < iClanTeammates )
  1162. return false;
  1163. if ( !steamapicontext->SteamFriends() || !steamapicontext->SteamUtils() || !g_pGameRules->IsMultiplayer() )
  1164. return false;
  1165. // determine local player team
  1166. int iLocalPlayerIndex = GetLocalPlayerIndex();
  1167. for ( int iClan = 0; iClan < steamapicontext->SteamFriends()->GetClanCount(); iClan++ )
  1168. {
  1169. int iClanMembersOnTeam = 0;
  1170. CSteamID clanID = steamapicontext->SteamFriends()->GetClanByIndex( iClan );
  1171. // enumerate all players
  1172. for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
  1173. {
  1174. if( ( iPlayerIndex != iLocalPlayerIndex ) && ( g_PR->IsConnected( iPlayerIndex ) ) )
  1175. {
  1176. player_info_t pi;
  1177. if ( engine->GetPlayerInfo( iPlayerIndex, &pi ) && ( pi.friendsID ) )
  1178. {
  1179. // check and see if they're on the local player's friends list
  1180. CSteamID steamID( pi.friendsID, 1, steamapicontext->SteamUtils()->GetConnectedUniverse(), k_EAccountTypeIndividual );
  1181. if ( steamapicontext->SteamFriends()->IsUserInSource( steamID, clanID ) )
  1182. {
  1183. iClanMembersOnTeam++;
  1184. if ( iClanMembersOnTeam == iClanTeammates )
  1185. return true;
  1186. }
  1187. }
  1188. }
  1189. }
  1190. }
  1191. #endif
  1192. return false;
  1193. }
  1194. else if ( IsGameConsole() )
  1195. {
  1196. // TODO: implement for 360
  1197. return false;
  1198. }
  1199. else
  1200. {
  1201. // other platforms...?
  1202. return false;
  1203. }
  1204. }
  1205. //-----------------------------------------------------------------------------
  1206. // Purpose: Returns the # of teammates of the local player
  1207. //-----------------------------------------------------------------------------
  1208. int CalcTeammateCount()
  1209. {
  1210. Assert( g_pGameRules->IsMultiplayer() );
  1211. // determine local player team
  1212. int iLocalPlayerIndex = GetLocalPlayerIndex();
  1213. int iLocalPlayerTeam = g_PR->GetTeam( iLocalPlayerIndex );
  1214. int iNumTeammates = 0;
  1215. for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
  1216. {
  1217. // find all players who are on the local player's team
  1218. if( ( iPlayerIndex != iLocalPlayerIndex ) && ( g_PR->IsConnected( iPlayerIndex ) ) && ( g_PR->GetTeam( iPlayerIndex ) == iLocalPlayerTeam ) )
  1219. {
  1220. iNumTeammates++;
  1221. }
  1222. }
  1223. return iNumTeammates;
  1224. }
  1225. //-----------------------------------------------------------------------------
  1226. // Purpose: Returns the # of teammates of the local player
  1227. //-----------------------------------------------------------------------------
  1228. int CalcPlayerCount()
  1229. {
  1230. int iCount = 0;
  1231. for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
  1232. {
  1233. // find all players who are on the local player's team
  1234. if( g_PR->IsConnected( iPlayerIndex ) )
  1235. {
  1236. iCount++;
  1237. }
  1238. }
  1239. return iCount;
  1240. }
  1241. #endif // CLIENT_DLL
  1242. ////-----------------------------------------------------------------------------
  1243. // Purpose: Resets all achievements. Behaves like ResetAchievements but does not automatically save profile
  1244. //-----------------------------------------------------------------------------
  1245. void CAchievementMgr::ClearAchievements( int nUserSlot )
  1246. {
  1247. if ( nUserSlot < 0 || nUserSlot >= MAX_SPLITSCREEN_PLAYERS )
  1248. {
  1249. AssertMsg( false, "CAchievementMgr::ClearAchievements invalid nUserSlot\n" );
  1250. return;
  1251. }
  1252. FOR_EACH_MAP( m_mapAchievement[nUserSlot], i )
  1253. {
  1254. CBaseAchievement *pAchievement = m_mapAchievement[nUserSlot][i];
  1255. ResetAchievement_Internal( pAchievement );
  1256. }
  1257. }
  1258. //-----------------------------------------------------------------------------
  1259. // Purpose: Resets all achievements. For debugging purposes only
  1260. //-----------------------------------------------------------------------------
  1261. void CAchievementMgr::ResetAchievements()
  1262. {
  1263. #if defined( CLIENT_DLL ) && !defined( NO_STEAM )
  1264. if ( !IsPC() )
  1265. {
  1266. DevMsg( "Only available on PC\n" );
  1267. return;
  1268. }
  1269. if ( !LoggedIntoSteam() )
  1270. {
  1271. Msg( "Steam not running, achievements disabled. Cannot reset achievements.\n" );
  1272. return;
  1273. }
  1274. FOR_EACH_MAP( m_mapAchievement[STEAM_PLAYER_SLOT], i )
  1275. {
  1276. CBaseAchievement *pAchievement = m_mapAchievement[STEAM_PLAYER_SLOT][i];
  1277. ResetAchievement_Internal( pAchievement );
  1278. }
  1279. if ( steamapicontext->SteamUserStats() )
  1280. {
  1281. steamapicontext->SteamUserStats()->StoreStats();
  1282. }
  1283. SaveGlobalState();
  1284. #endif // CLIENT_DLL
  1285. }
  1286. void CAchievementMgr::ResetAchievement( int iAchievementID )
  1287. {
  1288. #if defined( CLIENT_DLL ) && !defined( NO_STEAM )
  1289. if ( !IsPC() )
  1290. {
  1291. DevMsg( "Only available on PC\n" );
  1292. return;
  1293. }
  1294. if ( !LoggedIntoSteam() )
  1295. {
  1296. Msg( "Steam not running, achievements disabled. Cannot reset achievements.\n" );
  1297. return;
  1298. }
  1299. CBaseAchievement *pAchievement = GetAchievementByID( iAchievementID, STEAM_PLAYER_SLOT );
  1300. Assert( pAchievement );
  1301. if ( pAchievement )
  1302. {
  1303. ResetAchievement_Internal( pAchievement );
  1304. if ( steamapicontext->SteamUserStats() )
  1305. {
  1306. steamapicontext->SteamUserStats()->StoreStats();
  1307. }
  1308. SaveGlobalState();
  1309. }
  1310. #endif // CLIENT_DLL
  1311. }
  1312. //-----------------------------------------------------------------------------
  1313. // Purpose: Resets all achievements. For debugging purposes only
  1314. //-----------------------------------------------------------------------------
  1315. void CAchievementMgr::PrintAchievementStatus()
  1316. {
  1317. #if defined( CLIENT_DLL )
  1318. if ( IsPC() && !LoggedIntoSteam() )
  1319. {
  1320. Msg( "Steam not running, achievements disabled. Cannot view or unlock achievements.\n" );
  1321. return;
  1322. }
  1323. Msg( "%42s %-30s %s\n", "Name:", "Status:", "Point value:" );
  1324. int iTotalAchievements = 0, iTotalPoints = 0;
  1325. FOR_EACH_MAP( m_mapAchievement[STEAM_PLAYER_SLOT], i )
  1326. {
  1327. CBaseAchievement *pAchievement = m_mapAchievement[STEAM_PLAYER_SLOT][i];
  1328. Msg( "%42s ", pAchievement->GetName() );
  1329. CFailableAchievement *pFailableAchievement = dynamic_cast<CFailableAchievement *>( pAchievement );
  1330. if ( pAchievement->IsAchieved() )
  1331. {
  1332. CCSBaseAchievement* pCSAchievement = dynamic_cast<CCSBaseAchievement*>(pAchievement);
  1333. // Assign the award date text
  1334. char dateBuffer[32] = "";
  1335. int year, month, day, hour, minute, second;
  1336. if ( pCSAchievement && pCSAchievement->GetAwardTime(year, month, day, hour, minute, second) )
  1337. {
  1338. Q_snprintf( dateBuffer, sizeof(dateBuffer), "ACHIEVED %4d-%02d-%02d %2d:%02d:%02d", year, month, day, hour, minute, second );
  1339. Msg( "%-30s", dateBuffer );
  1340. }
  1341. else
  1342. {
  1343. Msg( "%-30s", "ACHIEVED" );
  1344. }
  1345. }
  1346. else if ( pFailableAchievement && pFailableAchievement->IsFailed() )
  1347. {
  1348. Msg( "%-30s", "FAILED" );
  1349. }
  1350. else
  1351. {
  1352. char szBuf[255];
  1353. char szComponents[67] = "";
  1354. int componentsLen = ARRAYSIZE( szComponents );
  1355. if ( pAchievement->HasComponents() )
  1356. {
  1357. uint64 bits = pAchievement->GetComponentBits();
  1358. int numComponents = pAchievement->GetNumComponents();
  1359. Q_strcat( szComponents, "(", componentsLen );
  1360. for ( int i=numComponents-1; i>=0; i-- )
  1361. {
  1362. Q_strcat( szComponents, ( bits & ((uint64)1<<i) ) ? "1" : "0", componentsLen );
  1363. }
  1364. Q_strcat( szComponents, ")", componentsLen );
  1365. }
  1366. Q_snprintf( szBuf, ARRAYSIZE( szBuf ), "(%d/%d)%s %s",
  1367. pAchievement->GetCount(), pAchievement->GetGoal(),
  1368. pAchievement->IsActive() ? "" : " (inactive)",
  1369. szComponents );
  1370. Msg( "%-30s", szBuf );
  1371. }
  1372. Msg( " %d ", pAchievement->GetPointValue() );
  1373. pAchievement->PrintAdditionalStatus();
  1374. Msg( "\n" );
  1375. iTotalAchievements++;
  1376. iTotalPoints += pAchievement->GetPointValue();
  1377. }
  1378. Msg( "Total achievements: %d Total possible points: %d\n", iTotalAchievements, iTotalPoints );
  1379. #endif
  1380. }
  1381. //-----------------------------------------------------------------------------
  1382. // Purpose: called when a game event is fired
  1383. //-----------------------------------------------------------------------------
  1384. void CAchievementMgr::FireGameEvent( IGameEvent *event )
  1385. {
  1386. #ifdef CLIENT_DLL
  1387. int nSplitScreenPlayer = event->GetInt( "splitscreenplayer" );
  1388. Assert(nSplitScreenPlayer < MAX_SPLITSCREEN_PLAYERS);
  1389. ACTIVE_SPLITSCREEN_PLAYER_GUARD( nSplitScreenPlayer );
  1390. #endif
  1391. VPROF_( "CAchievementMgr::FireGameEvent", 1, VPROF_BUDGETGROUP_STEAM, false, 0 );
  1392. const char *name = event->GetName();
  1393. if ( 0 == Q_strcmp( name, "entity_killed" ) )
  1394. {
  1395. #ifdef GAME_DLL
  1396. CBaseEntity *pVictim = UTIL_EntityByIndex( event->GetInt( "entindex_killed", 0 ) );
  1397. CBaseEntity *pAttacker = UTIL_EntityByIndex( event->GetInt( "entindex_attacker", 0 ) );
  1398. CBaseEntity *pInflictor = UTIL_EntityByIndex( event->GetInt( "entindex_inflictor", 0 ) );
  1399. OnKillEvent( pVictim, pAttacker, pInflictor, event );
  1400. #endif // GAME_DLL
  1401. }
  1402. else if ( 0 == Q_strcmp( name, "game_init" ) )
  1403. {
  1404. #ifdef GAME_DLL
  1405. // clear all state as though we were loading a saved game, but without loading the game
  1406. PreRestoreSavedGame();
  1407. PostRestoreSavedGame();
  1408. #endif // GAME_DLL
  1409. }
  1410. #ifdef CLIENT_DLL
  1411. else if ( 0 == Q_strcmp( name, "player_death" ) )
  1412. {
  1413. CBaseEntity *pVictim = ClientEntityList().GetEnt( engine->GetPlayerForUserID( event->GetInt("userid") ) );
  1414. CBaseEntity *pAttacker = ClientEntityList().GetEnt( engine->GetPlayerForUserID( event->GetInt("attacker") ) );
  1415. OnKillEvent( pVictim, pAttacker, NULL, event );
  1416. }
  1417. else if ( 0 == Q_strcmp( name, "localplayer_changeclass" ) )
  1418. {
  1419. // keep track of when the player last changed class
  1420. m_flLastClassChangeTime[nSplitScreenPlayer] = gpGlobals->curtime;
  1421. }
  1422. else if ( 0 == Q_strcmp( name, "localplayer_changeteam" ) )
  1423. {
  1424. // keep track of the time of transitions to and from a game team
  1425. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  1426. if ( pLocalPlayer )
  1427. {
  1428. int iTeam = pLocalPlayer->GetTeamNumber();
  1429. if ( iTeam > TEAM_SPECTATOR )
  1430. {
  1431. if ( 0 == m_flTeamplayStartTime[nSplitScreenPlayer] )
  1432. {
  1433. // player transitioned from no/spectator team to a game team, mark the time
  1434. m_flTeamplayStartTime[nSplitScreenPlayer] = gpGlobals->curtime;
  1435. }
  1436. }
  1437. else
  1438. {
  1439. // player transitioned to no/spectator team, clear the teamplay start time
  1440. m_flTeamplayStartTime[nSplitScreenPlayer] = 0;
  1441. }
  1442. }
  1443. }
  1444. else if ( 0 == Q_strcmp( name, "teamplay_round_start" ) )
  1445. {
  1446. if ( event->GetBool( "full_reset" ) )
  1447. {
  1448. // we're starting a full round, clear miniround count
  1449. m_iMiniroundsCompleted[nSplitScreenPlayer] = 0;
  1450. }
  1451. }
  1452. else if ( 0 == Q_strcmp( name, "teamplay_round_win" ) )
  1453. {
  1454. if ( false == event->GetBool( "full_round", true ) )
  1455. {
  1456. // we just finished a miniround but the round is continuing, increment miniround count
  1457. m_iMiniroundsCompleted[nSplitScreenPlayer]++;
  1458. }
  1459. }
  1460. else if ( 0 == Q_strcmp( name, "player_stats_updated" ) )
  1461. {
  1462. FOR_EACH_MAP( m_mapAchievement[nSplitScreenPlayer], i )
  1463. {
  1464. CBaseAchievement *pAchievement = m_mapAchievement[nSplitScreenPlayer][i];
  1465. pAchievement->OnPlayerStatsUpdate( nSplitScreenPlayer );
  1466. }
  1467. }
  1468. else if ( 0 == Q_strcmp( name, "read_game_titledata" ) )
  1469. {
  1470. SyncAchievementsToTitleData( event->GetInt( "controllerId" ), ACHIEVEMENT_READ_ACHIEVEMENT );
  1471. #if defined ( _X360 )
  1472. IGameEvent * repostEvent = gameeventmanager->CreateEvent( "repost_xbox_achievements" );
  1473. if ( repostEvent )
  1474. {
  1475. int userSlot = XBX_GetSlotByUserId( event->GetInt( "controllerId" ) );
  1476. if ( userSlot != -1 )
  1477. {
  1478. repostEvent->SetInt( "splitscreenplayer", userSlot );
  1479. gameeventmanager->FireEventClientSide( repostEvent );
  1480. }
  1481. }
  1482. #endif
  1483. }
  1484. else if ( 0 == Q_strcmp( name, "write_game_titledata" ) )
  1485. {
  1486. SyncAchievementsToTitleData( event->GetInt( "controllerId" ), ACHIEVEMENT_WRITE_ACHIEVEMENT );
  1487. }
  1488. else if ( 0 == Q_strcmp( name, "reset_game_titledata" ) )
  1489. {
  1490. ClearAchievements( XBX_GetSlotByUserId( event->GetInt( "controllerId" ) ) );
  1491. }
  1492. else if ( 0 == Q_strcmp( name, "write_profile_data" ) )
  1493. {
  1494. for ( int i=0; i<XUSER_MAX_COUNT; ++i )
  1495. {
  1496. char cmdLine[80];
  1497. Q_snprintf( cmdLine, 80, "host_writeconfig_ss %d", i );
  1498. engine->ClientCmd_Unrestricted( cmdLine );
  1499. }
  1500. }
  1501. else if ( 0 == Q_strcmp( name, "achievement_write_failed" ) )
  1502. {
  1503. #ifdef _GAMECONSOLE
  1504. // We didn't succeed and we're not waiting, so we failed
  1505. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  1506. "OnProfileUnavailable", "iController", XBX_GetUserId( nSplitScreenPlayer ) ) );
  1507. #endif
  1508. }
  1509. else if ( 0 == Q_strcmp( name, "user_data_downloaded" ) )
  1510. {
  1511. #ifndef NO_STEAM
  1512. UpdateStateFromSteam_Internal( nSplitScreenPlayer );
  1513. #endif
  1514. }
  1515. #endif // CLIENT_DLL
  1516. }
  1517. //-----------------------------------------------------------------------------
  1518. // Purpose: called when a player or character has been killed
  1519. //-----------------------------------------------------------------------------
  1520. void CAchievementMgr::OnKillEvent( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event )
  1521. {
  1522. #ifdef CLIENT_DLL
  1523. int nSplitScreenPlayer = SINGLE_PLAYER_SLOT;
  1524. nSplitScreenPlayer = event->GetInt( "splitscreenplayer" );
  1525. ACTIVE_SPLITSCREEN_PLAYER_GUARD( nSplitScreenPlayer );
  1526. #endif
  1527. // can have a NULL victim on client if victim has never entered local player's PVS
  1528. if ( !pVictim )
  1529. return;
  1530. // if single-player game, calculate if the attacker is the local player and if the victim is the player enemy
  1531. bool bAttackerIsPlayer = false;
  1532. bool bVictimIsPlayerEnemy = false;
  1533. #ifdef GAME_DLL
  1534. if ( !g_pGameRules->IsMultiplayer() )
  1535. {
  1536. CBasePlayer *pLocalPlayer = UTIL_GetLocalPlayer();
  1537. if ( pLocalPlayer )
  1538. {
  1539. if ( pAttacker == pLocalPlayer )
  1540. {
  1541. bAttackerIsPlayer = true;
  1542. }
  1543. CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pVictim );
  1544. if ( pBCC && ( D_HT == pBCC->IRelationType( pLocalPlayer ) ) )
  1545. {
  1546. bVictimIsPlayerEnemy = true;
  1547. }
  1548. }
  1549. }
  1550. #else
  1551. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  1552. bVictimIsPlayerEnemy = !pLocalPlayer->InSameTeam( pVictim );
  1553. if ( pAttacker == pLocalPlayer )
  1554. {
  1555. bAttackerIsPlayer = true;
  1556. }
  1557. #endif // GAME_DLL
  1558. for ( int j = 0; j < MAX_SPLITSCREEN_PLAYERS; ++j )
  1559. {
  1560. // look through all the kill event listeners and notify any achievements whose filters we pass
  1561. FOR_EACH_VEC( m_vecKillEventListeners[j], iAchievement )
  1562. {
  1563. CBaseAchievement *pAchievement = m_vecKillEventListeners[j][iAchievement];
  1564. if ( !pAchievement->IsActive() )
  1565. continue;
  1566. // if this achievement only looks for kills where attacker is player and that is not the case here, skip this achievement
  1567. if ( ( pAchievement->GetFlags() & ACH_FILTER_ATTACKER_IS_PLAYER ) && !bAttackerIsPlayer )
  1568. continue;
  1569. // if this achievement only looks for kills where victim is killer enemy and that is not the case here, skip this achievement
  1570. if ( ( pAchievement->GetFlags() & ACH_FILTER_VICTIM_IS_PLAYER_ENEMY ) && !bVictimIsPlayerEnemy )
  1571. continue;
  1572. #if GAME_DLL
  1573. // if this achievement only looks for a particular victim class name and this victim is a different class, skip this achievement
  1574. const char *pVictimClassNameFilter = pAchievement->m_pVictimClassNameFilter;
  1575. if ( pVictimClassNameFilter && !pVictim->ClassMatches( pVictimClassNameFilter ) )
  1576. continue;
  1577. // if this achievement only looks for a particular inflictor class name and this inflictor is a different class, skip this achievement
  1578. const char *pInflictorClassNameFilter = pAchievement->m_pInflictorClassNameFilter;
  1579. if ( pInflictorClassNameFilter && ( ( NULL == pInflictor ) || !pInflictor->ClassMatches( pInflictorClassNameFilter ) ) )
  1580. continue;
  1581. // if this achievement only looks for a particular attacker class name and this attacker is a different class, skip this achievement
  1582. const char *pAttackerClassNameFilter = pAchievement->m_pAttackerClassNameFilter;
  1583. if ( pAttackerClassNameFilter && ( ( NULL == pAttacker ) || !pAttacker->ClassMatches( pAttackerClassNameFilter ) ) )
  1584. continue;
  1585. // if this achievement only looks for a particular inflictor entity name and this inflictor has a different name, skip this achievement
  1586. const char *pInflictorEntityNameFilter = pAchievement->m_pInflictorEntityNameFilter;
  1587. if ( pInflictorEntityNameFilter && ( ( NULL == pInflictor ) || !pInflictor->NameMatches( pInflictorEntityNameFilter ) ) )
  1588. continue;
  1589. #endif // GAME_DLL
  1590. // we pass all filters for this achievement, notify the achievement of the kill
  1591. pAchievement->Event_EntityKilled( pVictim, pAttacker, pInflictor, event );
  1592. }
  1593. }
  1594. }
  1595. void CAchievementMgr::OnAchievementEvent( int iAchievementID, int nUserSlot )
  1596. {
  1597. // handle event for specific achievement
  1598. CBaseAchievement *pAchievement = GetAchievementByID( iAchievementID, nUserSlot );
  1599. Assert( pAchievement );
  1600. if ( pAchievement )
  1601. {
  1602. if ( !pAchievement->IsAchieved() )
  1603. {
  1604. pAchievement->IncrementCount();
  1605. }
  1606. }
  1607. }
  1608. //-----------------------------------------------------------------------------
  1609. // Purpose: called when a map-fired achievement event occurs
  1610. //-----------------------------------------------------------------------------
  1611. void CAchievementMgr::OnMapEvent( const char *pchEventName, int nUserSlot )
  1612. {
  1613. Assert( pchEventName && *pchEventName );
  1614. if ( !pchEventName || !*pchEventName )
  1615. return;
  1616. // see if this event matches the prefix for an achievement component
  1617. FOR_EACH_VEC( m_vecComponentListeners[nUserSlot], iAchievement )
  1618. {
  1619. CBaseAchievement *pAchievement = m_vecComponentListeners[nUserSlot][iAchievement];
  1620. Assert( pAchievement->m_pszComponentPrefix );
  1621. if ( 0 == Q_strncmp( pchEventName, pAchievement->m_pszComponentPrefix, pAchievement->m_iComponentPrefixLen ) )
  1622. {
  1623. // prefix matches, tell the achievement a component was found
  1624. pAchievement->OnComponentEvent( pchEventName );
  1625. return;
  1626. }
  1627. }
  1628. // look through all the map event listeners
  1629. FOR_EACH_VEC( m_vecMapEventListeners[nUserSlot], iAchievement )
  1630. {
  1631. CBaseAchievement *pAchievement = m_vecMapEventListeners[nUserSlot][iAchievement];
  1632. pAchievement->OnMapEvent( pchEventName );
  1633. }
  1634. }
  1635. //-----------------------------------------------------------------------------
  1636. // Purpose: Returns an achievement as it's abstract object. This interface is used by gameui.dll for getting achievement info.
  1637. // Input : index -
  1638. // Output : IBaseAchievement*
  1639. //-----------------------------------------------------------------------------
  1640. IAchievement* CAchievementMgr::GetAchievementByIndex( int index, int nUserSlot )
  1641. {
  1642. Assert( m_vecAchievement[nUserSlot].IsValidIndex(index) );
  1643. return (IAchievement*)m_vecAchievement[nUserSlot][index];
  1644. }
  1645. //-----------------------------------------------------------------------------
  1646. // Purpose: Returns an achievement as it's abstract object. This interface is used by gameui.dll for getting achievement info.
  1647. // Input : orderIndex -
  1648. // Output : IBaseAchievement*
  1649. //-----------------------------------------------------------------------------
  1650. IAchievement* CAchievementMgr::GetAchievementByDisplayOrder( int orderIndex, int nUserSlot )
  1651. {
  1652. Assert( m_vecAchievementInOrder[nUserSlot].IsValidIndex(orderIndex) );
  1653. return (IAchievement*)m_vecAchievementInOrder[nUserSlot][orderIndex];
  1654. }
  1655. //-----------------------------------------------------------------------------
  1656. // Purpose: Returns an asset award achievement as it's abstract object.
  1657. // Input : orderIndex -
  1658. // Output : IBaseAchievement*
  1659. //-----------------------------------------------------------------------------
  1660. IAchievement* CAchievementMgr::GetAwardByDisplayOrder( int orderIndex, int nUserSlot )
  1661. {
  1662. Assert( m_vecAwardInOrder[nUserSlot].IsValidIndex(orderIndex) );
  1663. return (IAchievement*)m_vecAwardInOrder[nUserSlot][orderIndex];
  1664. }
  1665. //-----------------------------------------------------------------------------
  1666. // Purpose: Returns total achievement count. This interface is used by gameui.dll for getting achievement info.
  1667. // Input : -
  1668. // Output : Count of achievements in manager's vector.
  1669. //-----------------------------------------------------------------------------
  1670. int CAchievementMgr::GetAchievementCount( bool bAssets )
  1671. {
  1672. int listCount = m_mapAchievement[SINGLE_PLAYER_SLOT].Count();
  1673. int achCount = 0;
  1674. for ( int i=0; i<listCount; ++i )
  1675. {
  1676. CBaseAchievement *pAchievement = m_mapAchievement[STEAM_PLAYER_SLOT][i];
  1677. if ( (bAssets && pAchievement->IsAssetAward()) || (!bAssets && !pAchievement->IsAssetAward()) )
  1678. {
  1679. achCount++;
  1680. }
  1681. }
  1682. return achCount;
  1683. }
  1684. //-----------------------------------------------------------------------------
  1685. // Purpose: Handles events from the matchmaking framework.
  1686. //-----------------------------------------------------------------------------
  1687. void CAchievementMgr::OnEvent( KeyValues *pEvent )
  1688. {
  1689. char const *szEvent = pEvent->GetName();
  1690. if ( FStrEq( szEvent, "OnProfileDataLoaded" ) )
  1691. {
  1692. // This event is sent when the title data blocks have been loaded.
  1693. int iController = pEvent->GetInt( "iController" );
  1694. #ifdef _GAMECONSOLE
  1695. int nSlot = XBX_GetSlotByUserId( iController );
  1696. #else
  1697. int nSlot = STEAM_PLAYER_SLOT;
  1698. #endif
  1699. ReadAchievementsFromTitleData( iController, nSlot );
  1700. }
  1701. else if ( FStrEq( szEvent, "sv_cheats_changed" ) )
  1702. { // we got a local event that sv_cheats value changed!
  1703. if ( pEvent->GetInt( "value" ) )
  1704. m_bCheatsEverOn = true;
  1705. }
  1706. #ifdef _GAMECONSOLE
  1707. else if ( FStrEq( szEvent, "OnProfilesChanged" ) )
  1708. {
  1709. // This is essentially a RESET
  1710. for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  1711. {
  1712. UserDisconnected( i );
  1713. }
  1714. // Mark the valid users as connected and try to download achievement data from LIVE
  1715. for ( unsigned int i = 0; i < XBX_GetNumGameUsers(); ++i )
  1716. {
  1717. UserConnected( i );
  1718. }
  1719. }
  1720. #endif
  1721. }
  1722. #if !defined(NO_STEAM)
  1723. //-----------------------------------------------------------------------------
  1724. // Purpose: called when stat upload is complete
  1725. // this needs to handle k_EResultInvalidParam which means that steam has rejected our uploaded stats
  1726. // and reset them with its copy. at that point we need to update our own copy of the stats to reflect steam's
  1727. //-----------------------------------------------------------------------------
  1728. void CAchievementMgr::Steam_OnUserStatsStored( UserStatsStored_t *pUserStatsStored )
  1729. {
  1730. #if !defined(GAME_DLL) && defined(USE_CEG)
  1731. Steamworks_TestSecret() ;
  1732. #endif
  1733. if ( k_EResultOK != pUserStatsStored->m_eResult )
  1734. {
  1735. DevMsg( "CAchievementMgr: Failed to upload stats to Steam, EResult %d!\n", pUserStatsStored->m_eResult );
  1736. }
  1737. else
  1738. {
  1739. #if defined( CLIENT_DLL )
  1740. UpdateStateFromSteam_Internal(STEAM_PLAYER_SLOT);
  1741. // send a message to the server about our achievements
  1742. if ( g_pGameRules && g_pGameRules->IsMultiplayer() )
  1743. {
  1744. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer( FirstValidSplitScreenSlot() );
  1745. if ( pLocalPlayer )
  1746. {
  1747. while ( m_AchievementsAwarded[STEAM_PLAYER_SLOT].Count() > 0 )
  1748. {
  1749. int nAchievementID = m_AchievementsAwarded[STEAM_PLAYER_SLOT].Head();
  1750. CBaseAchievement* pAchievement = GetAchievementByID( nAchievementID, STEAM_PLAYER_SLOT );
  1751. // verify that it is still achieved (it could have been rejected by Steam)
  1752. if ( pAchievement->IsAchieved() )
  1753. {
  1754. IGameEvent * event = gameeventmanager->CreateEvent( "achievement_earned" );
  1755. if ( event )
  1756. {
  1757. event->SetInt( "player", pLocalPlayer->entindex() );
  1758. event->SetInt( "achievement", nAchievementID );
  1759. gameeventmanager->FireEventClientSide( event );
  1760. }
  1761. }
  1762. m_AchievementsAwarded[STEAM_PLAYER_SLOT].RemoveMultipleFromHead(1);
  1763. }
  1764. }
  1765. }
  1766. #endif
  1767. // for each achievement that has not been achieved
  1768. FOR_EACH_MAP( m_mapAchievement[STEAM_PLAYER_SLOT], iAchievement )
  1769. {
  1770. CBaseAchievement *pAchievement = m_mapAchievement[STEAM_PLAYER_SLOT][iAchievement];
  1771. if ( !pAchievement->IsAchieved() )
  1772. {
  1773. pAchievement->OnSteamUserStatsStored();
  1774. }
  1775. }
  1776. }
  1777. }
  1778. #endif // !defined(NO_STEAM)
  1779. bool CAchievementMgr::SyncAchievementsToTitleData( int iController, SyncAchievementValueDirection_t eOp )
  1780. {
  1781. #if defined (_X360)
  1782. // get the local player
  1783. IPlayerLocal *pPlayerLocal = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iController );
  1784. if ( !pPlayerLocal )
  1785. return false;
  1786. TitleDataFieldsDescription_t const *pFields = g_pMatchFramework->GetMatchTitle()->DescribeTitleDataStorage();
  1787. // check version number
  1788. TitleDataFieldsDescription_t const *versionField = TitleDataFieldsDescriptionFindByString( pFields, "TITLEDATA.BLOCK2.VERSION" );
  1789. if ( !versionField || versionField->m_eDataType != TitleDataFieldsDescription_t::DT_uint16 )
  1790. {
  1791. Warning( "TITLEDATA.BLOCK2.VERSION is expected to be defined as DT_uint16\n" );
  1792. return false;
  1793. }
  1794. ConVarRef cl_titledataversionblock2( "cl_titledataversionblock2" );
  1795. if ( eOp == ACHIEVEMENT_READ_ACHIEVEMENT )
  1796. {
  1797. int versionNumber = TitleDataFieldsDescriptionGetValue<uint16>( versionField, pPlayerLocal );
  1798. if ( versionNumber != cl_titledataversionblock2.GetInt() )
  1799. {
  1800. Warning( "SyncAchievementsToTitleData incorrect verion #; got %d, expected %d\n", versionNumber, cl_titledataversionblock2.GetInt() );
  1801. return false;
  1802. }
  1803. }
  1804. else
  1805. {
  1806. TitleDataFieldsDescriptionSetValue<uint16>( versionField, pPlayerLocal,cl_titledataversionblock2.GetInt() );
  1807. }
  1808. bool bIsAchieved;
  1809. uint8 iochar;
  1810. char achName[ 256 ];
  1811. uint32 ioint;
  1812. int userSlot = XBX_GetSlotByUserId( iController );
  1813. Assert(userSlot < MAX_SPLITSCREEN_PLAYERS);
  1814. FOR_EACH_MAP( m_mapAchievement[userSlot], i )
  1815. {
  1816. CBaseAchievement *pAchievement = m_mapAchievement[userSlot][i];
  1817. Q_snprintf( achName, 255, "MEDALS.AWARDED%.3d", i );
  1818. TitleDataFieldsDescription_t const *pFieldAwarded = TitleDataFieldsDescriptionFindByString( pFields, achName );
  1819. Q_snprintf( achName, 255, "MEDALS.MEDALINFO%.3d", i );
  1820. TitleDataFieldsDescription_t const *pFieldMedalInfo = TitleDataFieldsDescriptionFindByString( pFields, achName );
  1821. if ( !pFieldAwarded || !pFieldMedalInfo )
  1822. {
  1823. continue;
  1824. }
  1825. if ( eOp == ACHIEVEMENT_WRITE_ACHIEVEMENT )
  1826. {
  1827. bIsAchieved = pAchievement->IsAchieved();
  1828. iochar = 0;
  1829. ioint = pAchievement->GetCount();
  1830. if ( bIsAchieved )
  1831. {
  1832. iochar = 2;
  1833. ioint = pAchievement->GetUnlockTime();
  1834. }
  1835. TitleDataFieldsDescriptionSetValue<uint8>( pFieldAwarded, pPlayerLocal, iochar );
  1836. TitleDataFieldsDescriptionSetValue<uint32>( pFieldMedalInfo, pPlayerLocal, ioint );
  1837. }
  1838. else
  1839. {
  1840. bIsAchieved = static_cast< bool >( TitleDataFieldsDescriptionGetValue<uint8>( pFieldAwarded, pPlayerLocal ) != 0 );
  1841. ioint = TitleDataFieldsDescriptionGetValue<uint32>( pFieldMedalInfo, pPlayerLocal );
  1842. if ( bIsAchieved )
  1843. {
  1844. pAchievement->SetUnlockTime( ioint );
  1845. pAchievement->SetAchieved( true );
  1846. }
  1847. else
  1848. {
  1849. pAchievement->SetAchieved( false );
  1850. pAchievement->SetCount( ioint );
  1851. }
  1852. }
  1853. }
  1854. if ( eOp == ACHIEVEMENT_READ_ACHIEVEMENT )
  1855. {
  1856. IGameEvent * event = gameeventmanager->CreateEvent( "achievement_info_loaded" );
  1857. if ( event )
  1858. {
  1859. gameeventmanager->FireEventClientSide( event );
  1860. }
  1861. }
  1862. #endif
  1863. return true;
  1864. }
  1865. void CAchievementMgr::ResetAchievement_Internal( CBaseAchievement *pAchievement )
  1866. {
  1867. #if defined( CLIENT_DLL )
  1868. Assert( pAchievement );
  1869. #if !defined ( NO_STEAM )
  1870. if ( steamapicontext->SteamUserStats() )
  1871. {
  1872. steamapicontext->SteamUserStats()->ClearAchievement( pAchievement->GetName() );
  1873. }
  1874. #endif
  1875. pAchievement->SetAchieved( false );
  1876. pAchievement->SetCount( 0 );
  1877. if ( pAchievement->HasComponents() )
  1878. {
  1879. pAchievement->SetComponentBits( 0 );
  1880. }
  1881. pAchievement->SetProgressShown( 0 );
  1882. pAchievement->StopListeningForAllEvents();
  1883. if ( pAchievement->IsActive() )
  1884. {
  1885. pAchievement->ListenForEvents();
  1886. }
  1887. #endif // CLIENT_DLL
  1888. }
  1889. #ifdef CLIENT_DLL
  1890. bool MsgFunc_AchievementEvent( const CCSUsrMsg_AchievementEvent &msg )
  1891. {
  1892. int iAchievementID = (int) msg.achievement();
  1893. CAchievementMgr *pAchievementMgr = CAchievementMgr::GetInstance();
  1894. if ( !pAchievementMgr )
  1895. return true;
  1896. int iCount = (int) msg.count();
  1897. int userID = (int) msg.user_id();
  1898. // Fix unused variable warning
  1899. NOTE_UNUSED(iCount);
  1900. int userSlot = STEAM_PLAYER_SLOT;
  1901. #if defined ( _X360 )
  1902. for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  1903. {
  1904. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(i);
  1905. if ( pLocalPlayer && !pLocalPlayer->IsNPC() )
  1906. {
  1907. if ( pLocalPlayer->GetUserID() == userID )
  1908. {
  1909. userSlot = i;
  1910. }
  1911. }
  1912. }
  1913. #else
  1914. NOTE_UNUSED(userID);
  1915. #endif // _X360
  1916. pAchievementMgr->OnAchievementEvent( iAchievementID, userSlot );
  1917. return true;
  1918. }
  1919. #if ALLOW_ACHIEVEMENTS_WITH_CHEATS
  1920. CON_COMMAND_F( achievement_reset_all, "Clears all achievements", FCVAR_DEVELOPMENTONLY )
  1921. {
  1922. CAchievementMgr *pAchievementMgr = CAchievementMgr::GetInstance();
  1923. if ( !pAchievementMgr )
  1924. return;
  1925. pAchievementMgr->ResetAchievements();
  1926. }
  1927. int StringSortFunc( const void *p1, const void *p2 )
  1928. {
  1929. const char *psz1 = (const char *)p1;
  1930. const char *psz2 = (const char *)p2;
  1931. return ( Q_strcmp( psz1, psz2 ) );
  1932. }
  1933. static int AchievementNameCompletion( const char *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  1934. {
  1935. CAchievementMgr *pAchievementMgr = CAchievementMgr::GetInstance();
  1936. if ( !pAchievementMgr )
  1937. return 0;
  1938. const char *firstSpace = V_strstr( partial, " " );
  1939. if ( !firstSpace )
  1940. return 0;
  1941. // store the command
  1942. char commandName[COMMAND_COMPLETION_ITEM_LENGTH];
  1943. int commandLength = firstSpace - partial;
  1944. V_StrSlice( partial, 0, commandLength, commandName, sizeof( commandName ) );
  1945. // skip the command
  1946. partial += commandLength + 1;
  1947. int partialLength = Q_strlen( partial );
  1948. if ( !partial )
  1949. return 0;
  1950. int numMatches = 0;
  1951. CUtlMap< int, CBaseAchievement *> &achievements = pAchievementMgr->GetAchievements( STEAM_PLAYER_SLOT );
  1952. for ( int i = achievements.FirstInorder(); i != achievements.InvalidIndex() && numMatches < COMMAND_COMPLETION_MAXITEMS; i = achievements.NextInorder( i ) )
  1953. {
  1954. CBaseAchievement *pAchievement = achievements[i];
  1955. const char *pszAchievementName = pAchievement->GetName();
  1956. if ( !Q_strncasecmp( pszAchievementName, partial, partialLength ) )
  1957. {
  1958. Q_snprintf( commands[ numMatches ], sizeof( commands[ numMatches ] ), "%s %s", commandName, pszAchievementName );
  1959. numMatches++;
  1960. }
  1961. }
  1962. // sort the commands
  1963. qsort( commands, numMatches, COMMAND_COMPLETION_ITEM_LENGTH, StringSortFunc );
  1964. return numMatches;
  1965. }
  1966. CON_COMMAND_F_COMPLETION( achievement_reset, "<internal name> Clears specified achievement", FCVAR_DEVELOPMENTONLY, AchievementNameCompletion )
  1967. {
  1968. CAchievementMgr *pAchievementMgr = CAchievementMgr::GetInstance();
  1969. if ( !pAchievementMgr )
  1970. return;
  1971. if ( 2 != args.ArgC() )
  1972. {
  1973. Msg( "Usage: achievement_reset <internal name>\n" );
  1974. return;
  1975. }
  1976. CBaseAchievement *pAchievement = pAchievementMgr->GetAchievementByName( args[1], STEAM_PLAYER_SLOT );
  1977. if ( !pAchievement )
  1978. {
  1979. Msg( "Achievement %s not found\n", args[1] );
  1980. return;
  1981. }
  1982. pAchievementMgr->ResetAchievement( pAchievement->GetAchievementID() );
  1983. }
  1984. CON_COMMAND_F( achievement_status, "Shows status of all achievement", FCVAR_DEVELOPMENTONLY )
  1985. {
  1986. CAchievementMgr *pAchievementMgr = CAchievementMgr::GetInstance();
  1987. if ( !pAchievementMgr )
  1988. return;
  1989. pAchievementMgr->PrintAchievementStatus();
  1990. }
  1991. CON_COMMAND_F_COMPLETION( achievement_unlock, "<internal name> Unlocks achievement", FCVAR_DEVELOPMENTONLY, AchievementNameCompletion )
  1992. {
  1993. CAchievementMgr *pAchievementMgr = CAchievementMgr::GetInstance();
  1994. if ( !pAchievementMgr )
  1995. return;
  1996. if ( 2 != args.ArgC() )
  1997. {
  1998. Msg( "Usage: achievement_unlock <internal name>\n" );
  1999. return;
  2000. }
  2001. CBaseAchievement *pAchievement = pAchievementMgr->GetAchievementByName( args[1], STEAM_PLAYER_SLOT );
  2002. if ( !pAchievement )
  2003. {
  2004. Msg( "Achievement %s not found\n", args[1] );
  2005. return;
  2006. }
  2007. pAchievementMgr->AwardAchievement( pAchievement->GetAchievementID(), STEAM_PLAYER_SLOT );
  2008. }
  2009. CON_COMMAND_F( achievement_unlock_all, "Unlocks all achievements", FCVAR_DEVELOPMENTONLY )
  2010. {
  2011. CAchievementMgr *pAchievementMgr = CAchievementMgr::GetInstance();
  2012. if ( !pAchievementMgr )
  2013. return;
  2014. int iCount = pAchievementMgr->GetAchievementCount();
  2015. for ( int i = 0; i < iCount; i++ )
  2016. {
  2017. IAchievement *pAchievement = pAchievementMgr->GetAchievementByIndex( i, STEAM_PLAYER_SLOT );
  2018. if ( !pAchievement->IsAchieved() )
  2019. {
  2020. pAchievementMgr->AwardAchievement( pAchievement->GetAchievementID(), STEAM_PLAYER_SLOT );
  2021. }
  2022. }
  2023. }
  2024. CON_COMMAND_F_COMPLETION( achievement_evaluate, "<internal name> Causes failable achievement to be evaluated", FCVAR_DEVELOPMENTONLY, AchievementNameCompletion )
  2025. {
  2026. CAchievementMgr *pAchievementMgr = CAchievementMgr::GetInstance();
  2027. if ( !pAchievementMgr )
  2028. return;
  2029. if ( 2 != args.ArgC() )
  2030. {
  2031. Msg( "Usage: achievement_evaluate <internal name>\n" );
  2032. return;
  2033. }
  2034. CBaseAchievement *pAchievement = pAchievementMgr->GetAchievementByName( args[1], STEAM_PLAYER_SLOT );
  2035. if ( !pAchievement )
  2036. {
  2037. Msg( "Achievement %s not found\n", args[1] );
  2038. return;
  2039. }
  2040. CFailableAchievement *pFailableAchievement = dynamic_cast<CFailableAchievement *>( pAchievement );
  2041. Assert( pFailableAchievement );
  2042. if ( pFailableAchievement )
  2043. {
  2044. pFailableAchievement->OnEvaluationEvent();
  2045. }
  2046. }
  2047. CON_COMMAND_F( achievement_test_friend_count, "Counts the # of teammates on local player's friends list", FCVAR_DEVELOPMENTONLY )
  2048. {
  2049. CAchievementMgr *pAchievementMgr = CAchievementMgr::GetInstance();
  2050. if ( !pAchievementMgr )
  2051. return;
  2052. if ( 2 != args.ArgC() )
  2053. {
  2054. Msg( "Usage: achievement_test_friend_count <min # of teammates>\n" );
  2055. return;
  2056. }
  2057. int iMinFriends = atoi( args[1] );
  2058. bool bRet = CalcPlayersOnFriendsList( iMinFriends );
  2059. Msg( "You %s have at least %d friends in the game.\n", bRet ? "do" : "do not", iMinFriends );
  2060. }
  2061. CON_COMMAND_F( achievement_test_clan_count, "Determines if specified # of teammates belong to same clan w/local player", FCVAR_DEVELOPMENTONLY )
  2062. {
  2063. CAchievementMgr *pAchievementMgr = CAchievementMgr::GetInstance();
  2064. if ( !pAchievementMgr )
  2065. return;
  2066. if ( 2 != args.ArgC() )
  2067. {
  2068. Msg( "Usage: achievement_test_clan_count <# of clan teammates>\n" );
  2069. return;
  2070. }
  2071. int iClanPlayers = atoi( args[1] );
  2072. bool bRet = CalcHasNumClanPlayers( iClanPlayers );
  2073. Msg( "There %s %d players who you're in a Steam group with.\n", bRet ? "are" : "are not", iClanPlayers );
  2074. }
  2075. CON_COMMAND_F( achievement_mark_dirty, "Mark achievement data as dirty", FCVAR_DEVELOPMENTONLY )
  2076. {
  2077. CAchievementMgr *pAchievementMgr = CAchievementMgr::GetInstance();
  2078. if ( !pAchievementMgr )
  2079. return;
  2080. pAchievementMgr->SetDirty( true, STEAM_PLAYER_SLOT );
  2081. }
  2082. #endif // _DEBUG
  2083. #endif // CLIENT_DLL
  2084. //-----------------------------------------------------------------------------
  2085. // Purpose: helper function to get entity model name
  2086. //-----------------------------------------------------------------------------
  2087. const char *GetModelName( CBaseEntity *pBaseEntity )
  2088. {
  2089. CBaseAnimating *pBaseAnimating = dynamic_cast<CBaseAnimating *>( pBaseEntity );
  2090. if ( pBaseAnimating )
  2091. {
  2092. CStudioHdr *pStudioHdr = pBaseAnimating->GetModelPtr();
  2093. if ( pStudioHdr )
  2094. {
  2095. return pStudioHdr->pszName();
  2096. }
  2097. }
  2098. return "";
  2099. }
  2100. //-----------------------------------------------------------------------------
  2101. // Purpose: Gets the list of achievements achieved during the current game
  2102. //-----------------------------------------------------------------------------
  2103. const CUtlVector<int>& CAchievementMgr::GetAchievedDuringCurrentGame( int nPlayerSlot )
  2104. {
  2105. return m_AchievementsAwardedDuringCurrentGame[nPlayerSlot];
  2106. }
  2107. //-----------------------------------------------------------------------------
  2108. // Purpose: Reset the list of achievements achieved during the current game
  2109. //-----------------------------------------------------------------------------
  2110. void CAchievementMgr::ResetAchievedDuringCurrentGame( int nPlayerSlot )
  2111. {
  2112. m_AchievementsAwardedDuringCurrentGame[nPlayerSlot].RemoveAll();
  2113. }
  2114. //-----------------------------------------------------------------------------
  2115. // Purpose: Clears achievement data for the a particular user slot
  2116. //-----------------------------------------------------------------------------
  2117. void CAchievementMgr::ClearAchievementData( int nUserSlot )
  2118. {
  2119. Assert(nUserSlot < MAX_SPLITSCREEN_PLAYERS);
  2120. FOR_EACH_MAP_FAST( m_mapAchievement[nUserSlot], i )
  2121. {
  2122. m_mapAchievement[nUserSlot][i]->ClearAchievementData();
  2123. }
  2124. }
  2125. //-----------------------------------------------------------------------------
  2126. // Purpose: Do per-frame handling
  2127. //-----------------------------------------------------------------------------
  2128. void CAchievementMgr::Update( float frametime )
  2129. {
  2130. #ifdef CLIENT_DLL
  2131. if ( !sv_cheats )
  2132. {
  2133. sv_cheats = cvar->FindVar( "sv_cheats" );
  2134. }
  2135. #endif
  2136. #ifndef _DEBUG
  2137. // keep track if cheats have ever been turned on during this level
  2138. if ( !WereCheatsEverOn() )
  2139. {
  2140. if ( sv_cheats && sv_cheats->GetBool() )
  2141. {
  2142. m_bCheatsEverOn = true;
  2143. }
  2144. }
  2145. #endif
  2146. // [sbodenbender] brought over from orange box
  2147. // Call think functions. Work backwards, because we may remove achievements from the list.
  2148. int iCount = m_vecThinkListeners.Count();
  2149. for ( int i = iCount-1; i >= 0; i-- )
  2150. {
  2151. if ( m_vecThinkListeners[i].m_flThinkTime < gpGlobals->curtime )
  2152. {
  2153. m_vecThinkListeners[i].pAchievement->Think();
  2154. // The think function may have pushed out the think time. If not, remove ourselves from the list.
  2155. if ( m_vecThinkListeners[i].pAchievement->IsAchieved() || m_vecThinkListeners[i].m_flThinkTime < gpGlobals->curtime )
  2156. {
  2157. m_vecThinkListeners.Remove(i);
  2158. }
  2159. }
  2160. }
  2161. #ifdef _X360
  2162. bool bWarningShown = false;
  2163. for ( int i = m_pendingAchievementState.Count()-1; i >= 0; i-- ) // Iterate backwards to make deletion safe
  2164. {
  2165. // Check for a pending achievement write
  2166. uint nResultCode;
  2167. int nReturn = xboxsystem->GetOverlappedResult( m_pendingAchievementState[i].pOverlappedResult, &nResultCode, false );
  2168. if ( nReturn == ERROR_IO_PENDING || nReturn == ERROR_IO_INCOMPLETE )
  2169. continue;
  2170. // We are attempting to grant an achievement.
  2171. if ( nReturn != ERROR_SUCCESS )
  2172. {
  2173. // The achievement write has failed.
  2174. if ( bWarningShown == false )
  2175. {
  2176. // Create a game message to pop up a warning to the user
  2177. IGameEvent *event = gameeventmanager->CreateEvent( "achievement_write_failed" );
  2178. if ( event )
  2179. {
  2180. gameeventmanager->FireEvent( event );
  2181. bWarningShown = true;
  2182. }
  2183. }
  2184. // We need to unaward the achievement in this case!
  2185. CBaseAchievement *pAchievement = GetAchievementByID( m_pendingAchievementState[i].nAchievementID, m_pendingAchievementState[i].nUserSlot );
  2186. if ( pAchievement != NULL )
  2187. {
  2188. pAchievement->SetAchieved( false );
  2189. m_bDirty[m_pendingAchievementState[i].nUserSlot] = true;
  2190. m_AchievementsAwardedDuringCurrentGame->FindAndRemove( m_pendingAchievementState[i].nAchievementID );
  2191. // FIXME: This doesn't account for incremental progress, but if *will* re-achieve these if you get them again
  2192. }
  2193. }
  2194. // We've either succeeded or failed at this point, in both cases we don't care anymore!
  2195. xboxsystem->ReleaseAsyncHandle( m_pendingAchievementState[i].pOverlappedResult );
  2196. m_pendingAchievementState.FastRemove( i );
  2197. }
  2198. #endif // _X360
  2199. }
  2200. bool CAchievementMgr::IsCurrentMap( const char *pszMapName )
  2201. {
  2202. if ( !pszMapName )
  2203. return false;
  2204. return StringHasPrefixCaseSensitive( GetMapName(), pszMapName );
  2205. }