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.

891 lines
29 KiB

  1. //====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "achievementmgr.h"
  8. #include "icommandline.h"
  9. #ifdef CLIENT_DLL
  10. #include "tier3/tier3.h"
  11. #include "vgui/ILocalize.h"
  12. #include "achievement_notification_panel.h"
  13. #include "fmtstr.h"
  14. #include "cdll_client_int.h"
  15. #include "matchmaking/imatchframework.h"
  16. #endif // CLIENT_DLL
  17. #include <vgui/ISystem.h>
  18. #include "../../src/public/vgui_controls/Controls.h"
  19. // NOTE: This has to be the last file included!
  20. #include "tier0/memdbgon.h"
  21. CBaseAchievementHelper *CBaseAchievementHelper::s_pFirst = NULL;
  22. static int g_nAchivementBitchCount = 0;
  23. BEGIN_DATADESC_NO_BASE( CBaseAchievement )
  24. DEFINE_FIELD( m_iCount, FIELD_INTEGER ),
  25. END_DATADESC()
  26. BEGIN_DATADESC( CFailableAchievement )
  27. DEFINE_FIELD( m_bActivated, FIELD_BOOLEAN ),
  28. DEFINE_FIELD( m_bFailed, FIELD_BOOLEAN ),
  29. END_DATADESC()
  30. //-----------------------------------------------------------------------------
  31. // Purpose: constructor
  32. //-----------------------------------------------------------------------------
  33. CBaseAchievement::CBaseAchievement()
  34. {
  35. m_iFlags = 0;
  36. m_iGoal = 0;
  37. m_iProgressMsgIncrement = 0;
  38. m_iProgressMsgMinimum = 0;
  39. m_iAchievementID = 0;
  40. m_iPointValue = 0;
  41. m_bHideUntilAchieved = false;
  42. m_bStoreProgressInSteam = false;
  43. m_pVictimClassNameFilter = NULL;
  44. m_pAttackerClassNameFilter = NULL;
  45. m_pInflictorClassNameFilter = NULL;
  46. m_pInflictorEntityNameFilter = NULL;
  47. m_pMapNameFilter = NULL;
  48. m_pGameDirFilter = NULL;
  49. m_pszComponentNames = NULL;
  50. m_pszComponentDisplayNames = NULL;
  51. m_pszComponentPrefix = NULL;
  52. m_iNumComponents = 0;
  53. m_iComponentPrefixLen = 0;
  54. m_iComponentBits = 0;
  55. m_iCount = 0;
  56. m_iProgressShown = 0;
  57. m_bAchieved = false;
  58. m_uUnlockTime = 0;
  59. m_pAchievementMgr = NULL;
  60. m_nUserSlot = 0;
  61. m_iAssetAwardID = 0;
  62. m_bShowOnHUD = false;
  63. }
  64. //-----------------------------------------------------------------------------
  65. // Purpose: sets flags
  66. //-----------------------------------------------------------------------------
  67. void CBaseAchievement::SetFlags( int iFlags )
  68. {
  69. // must always specify a save method
  70. Assert( iFlags & ( ACH_SAVE_WITH_GAME | ACH_SAVE_GLOBAL ) );
  71. m_iFlags = iFlags;
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Purpose: called when a game event being listened for is dispatched
  75. //-----------------------------------------------------------------------------
  76. void CBaseAchievement::FireGameEvent( IGameEvent *event )
  77. {
  78. #ifdef CLIENT_DLL
  79. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nUserSlot );
  80. #endif
  81. //
  82. // Perform common filtering to make it simpler to write achievements
  83. //
  84. if ( !IsActive() )
  85. {
  86. return;
  87. }
  88. // if the achievement only applies to a specific map, and it's not the current map, skip it
  89. if ( m_pMapNameFilter && ( 0 != Q_strcmp( m_pAchievementMgr->GetMapName(), m_pMapNameFilter ) ) )
  90. return;
  91. const char *name = event->GetName();
  92. if ( 0 == Q_strcmp( name, "teamplay_round_win" ) )
  93. {
  94. // if this is a round win and the achievement wants full round events only, filter this out
  95. // if this is not the end of a full round
  96. if ( ( m_iFlags & ACH_FILTER_FULL_ROUND_ONLY ) && ( false == event->GetBool( "full_round" ) ) )
  97. return;
  98. }
  99. // let the achievement handle the event
  100. FireGameEvent_Internal( event );
  101. }
  102. //-----------------------------------------------------------------------------
  103. // Purpose: sets victim class to filter with
  104. //-----------------------------------------------------------------------------
  105. void CBaseAchievement::SetVictimFilter( const char *pClassName )
  106. {
  107. m_pVictimClassNameFilter = pClassName;
  108. }
  109. //-----------------------------------------------------------------------------
  110. // Purpose: sets attacker class to filter with
  111. //-----------------------------------------------------------------------------
  112. void CBaseAchievement::SetAttackerFilter( const char *pClassName )
  113. {
  114. m_pAttackerClassNameFilter = pClassName;
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Purpose: sets inflictor class to filter with
  118. //-----------------------------------------------------------------------------
  119. void CBaseAchievement::SetInflictorFilter( const char *pClassName )
  120. {
  121. m_pInflictorClassNameFilter = pClassName;
  122. }
  123. //-----------------------------------------------------------------------------
  124. // Purpose: sets inflictor entity name to filter with
  125. //-----------------------------------------------------------------------------
  126. void CBaseAchievement::SetInflictorEntityNameFilter( const char *pEntityName )
  127. {
  128. m_pInflictorEntityNameFilter = pEntityName;
  129. }
  130. //-----------------------------------------------------------------------------
  131. // Purpose: sets map name to filter with
  132. //-----------------------------------------------------------------------------
  133. void CBaseAchievement::SetMapNameFilter( const char *pMapName )
  134. {
  135. m_pMapNameFilter = pMapName;
  136. }
  137. //-----------------------------------------------------------------------------
  138. // Purpose: sets game dir to filter with. Note: in general, achievements should
  139. // only be compiled into products they pertain to. But if there are
  140. // any game-specific achievements which need to be in a binary shared
  141. // across products (e.g. Ep1 & Ep2), use the game dir as a runtime
  142. // filter.
  143. //-----------------------------------------------------------------------------
  144. void CBaseAchievement::SetGameDirFilter( const char *pGameDir )
  145. {
  146. m_pGameDirFilter = pGameDir;
  147. }
  148. //-----------------------------------------------------------------------------
  149. // Purpose: sets prefix to look for in map event string to identify a component
  150. // for this achievement
  151. //-----------------------------------------------------------------------------
  152. void CBaseAchievement::SetComponentPrefix( const char *pPrefix )
  153. {
  154. m_pszComponentPrefix = pPrefix;
  155. m_iComponentPrefixLen = Q_strlen( pPrefix );
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Purpose: called when a kill that passes filter critera occurs. This
  159. // is the default implementation, achievements can override to
  160. // do special handling
  161. //-----------------------------------------------------------------------------
  162. void CBaseAchievement::Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event )
  163. {
  164. // extra paranoid check: should only get here if registered as a kill event listener
  165. Assert( GetFlags() & ACH_LISTEN_KILL_EVENTS );
  166. if ( !( GetFlags() & ACH_LISTEN_KILL_EVENTS ) )
  167. return;
  168. // default implementation is just to increase count when filter criteria pass
  169. IncrementCount();
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose: called when an event that counts toward an achievement occurs
  173. //-----------------------------------------------------------------------------
  174. void CBaseAchievement::IncrementCount( int iOptIncrement )
  175. {
  176. if ( !IsAchieved() )
  177. {
  178. if ( !CheckAchievementsEnabled() )
  179. {
  180. #ifndef _DEBUG
  181. if ( g_nAchivementBitchCount++ < 10 )
  182. {
  183. DevMsg( "Achievements disabled, ignoring achievement progress for %s\n", GetName() );
  184. }
  185. #endif
  186. return;
  187. }
  188. // on client, where the count is kept, increment count
  189. if ( iOptIncrement > 0 )
  190. {
  191. // user specified that we want to increase by more than one.
  192. m_iCount += iOptIncrement;
  193. if ( m_iCount > m_iGoal )
  194. {
  195. m_iCount = m_iGoal;
  196. }
  197. }
  198. else
  199. {
  200. m_iCount++;
  201. }
  202. // if this achievement gets saved w/global state, flag our global state as dirty
  203. if ( GetFlags() & ACH_SAVE_GLOBAL )
  204. {
  205. m_pAchievementMgr->SetDirty( true, m_nUserSlot );
  206. }
  207. if ( cc_achievement_debug.GetInt() )
  208. {
  209. Msg( "Achievement count increased for %s: %d/%d\n", GetName(), m_iCount, m_iGoal );
  210. }
  211. #if 0 // !defined( NO_STEAM ) - <vitaliy> achievements should always go via TitleData
  212. // if this achievement's progress should be stored in Steam, set the steam stat for it
  213. if ( StoreProgressInSteam() && steamapicontext->SteamUserStats() )
  214. {
  215. // Set the Steam stat with the same name as the achievement. Only cached locally until we upload it.
  216. char pszProgressName[1024];
  217. Q_snprintf( pszProgressName, 1024, "%s_STAT", GetName() );
  218. bool bRet = steamapicontext->SteamUserStats()->SetStat( pszProgressName, m_iCount );
  219. if ( !bRet )
  220. {
  221. DevMsg( "ISteamUserStats::GetStat failed to set progress value in Steam for achievement %s\n", pszProgressName );
  222. }
  223. #ifdef INFESTED_DLL
  224. // Upload user data to commit the change to Steam so if the client crashes, progress isn't lost.
  225. // Only upload if we haven't uploaded recently, to keep us from spamming Steam with uploads. If we don't
  226. // upload now, it will get uploaded no later than level shutdown.
  227. if ( ( m_pAchievementMgr->GetTimeLastUpload() == 0 ) || ( Plat_FloatTime() - m_pAchievementMgr->GetTimeLastUpload() > 60 * 15 ) )
  228. {
  229. m_pAchievementMgr->UploadUserData( STEAM_PLAYER_SLOT );
  230. }
  231. #else
  232. m_pAchievementMgr->SetDirty( true, m_nUserSlot );
  233. #endif
  234. }
  235. #endif
  236. // if we've hit goal, award the achievement
  237. if ( m_iGoal > 0 )
  238. {
  239. if ( m_iCount >= m_iGoal )
  240. {
  241. AwardAchievement();
  242. }
  243. else
  244. {
  245. HandleProgressUpdate();
  246. }
  247. }
  248. }
  249. }
  250. void CBaseAchievement::SetShowOnHUD( bool bShow )
  251. {
  252. if ( m_bShowOnHUD != bShow )
  253. {
  254. m_pAchievementMgr->SetDirty( true, m_nUserSlot );
  255. }
  256. m_bShowOnHUD = bShow;
  257. }
  258. void CBaseAchievement::HandleProgressUpdate()
  259. {
  260. // if we've hit the right # of progress steps to show a progress notification, show it
  261. if ( ( m_iProgressMsgIncrement > 0 ) && m_iCount >= m_iProgressMsgMinimum && ( 0 == ( m_iCount % m_iProgressMsgIncrement ) ) )
  262. {
  263. // which notification is this
  264. int iProgress = m_iCount / m_iProgressMsgIncrement;
  265. // if we haven't already shown this progress step, show it
  266. if ( iProgress > m_iProgressShown )
  267. {
  268. ShowProgressNotification();
  269. // remember progress step shown so we don't show it again if the player loads an earlier save game
  270. // and gets past this point again
  271. m_iProgressShown = iProgress;
  272. m_pAchievementMgr->SetDirty( true, m_nUserSlot );
  273. }
  274. }
  275. }
  276. //-----------------------------------------------------------------------------
  277. // Purpose: calculates at how many steps we should show a progress notification
  278. //-----------------------------------------------------------------------------
  279. void CBaseAchievement::CalcProgressMsgIncrement()
  280. {
  281. // by default, show progress at every 25%
  282. m_iProgressMsgIncrement = m_iGoal / 4;
  283. // if goal is not evenly divisible by 4, try some other values
  284. if ( 0 != ( m_iGoal % 4 ) )
  285. {
  286. if ( 0 == ( m_iGoal % 3 ) )
  287. {
  288. // if evenly divisible by 3, use that
  289. m_iProgressMsgIncrement = m_iGoal / 3;
  290. }
  291. else if ( 0 == ( m_iGoal % 5 ) )
  292. {
  293. // if evenly divisible by 5, use that
  294. m_iProgressMsgIncrement = m_iGoal / 5;
  295. }
  296. // otherwise stick with divided by 4, rounded off
  297. }
  298. // don't show progress notifications for less than 5 things
  299. if ( m_iProgressMsgIncrement < 5 )
  300. {
  301. m_iProgressMsgIncrement = 0;
  302. }
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Purpose:
  306. //-----------------------------------------------------------------------------
  307. void CBaseAchievement::SetNextThink( float flThinkTime )
  308. {
  309. m_pAchievementMgr->SetAchievementThink( this, flThinkTime );
  310. }
  311. //-----------------------------------------------------------------------------
  312. // Purpose:
  313. //-----------------------------------------------------------------------------
  314. void CBaseAchievement::ClearThink( void )
  315. {
  316. m_pAchievementMgr->SetAchievementThink( this, THINK_CLEAR );
  317. }
  318. //-----------------------------------------------------------------------------
  319. // Purpose: see if we should award an achievement based on what just happened
  320. //-----------------------------------------------------------------------------
  321. void CBaseAchievement::EvaluateNewAchievement()
  322. {
  323. if ( !IsAchieved() && m_iGoal > 0 && m_iCount >= m_iGoal )
  324. {
  325. m_pAchievementMgr->CheckSignInState( false ); // Local user isn't resolvable, but we know we are signed in (our caller checks).
  326. AwardAchievement();
  327. m_pAchievementMgr->CheckSignInState( true );
  328. CheckAssetAwards( m_nUserSlot ); // Since we awarded the achievement we might need to award associated assets as well
  329. }
  330. }
  331. //-----------------------------------------------------------------------------
  332. // Purpose: determine if we should set this achievement to be achieved based
  333. // on other state. Used at init time.
  334. //-----------------------------------------------------------------------------
  335. void CBaseAchievement::EvaluateIsAlreadyAchieved()
  336. {
  337. // Check platform specific data to determine status of completed achievements.
  338. #if !defined(NO_STEAM)
  339. ISteamUserStats *pSteamUserStats = steamapicontext->SteamUserStats();
  340. if ( pSteamUserStats )
  341. {
  342. bool bAchieved;
  343. pSteamUserStats->GetAchievement( GetName(), &bAchieved );
  344. SetAchieved( bAchieved );
  345. }
  346. // Handled in CAchievementMgr::UserConnected on X360
  347. #endif
  348. EvaluateNewAchievement();
  349. }
  350. //-----------------------------------------------------------------------------
  351. // Purpose: called a map event for this achievement occurs
  352. //-----------------------------------------------------------------------------
  353. void CBaseAchievement::OnMapEvent( const char *pEventName )
  354. {
  355. Assert( m_iFlags & ACH_LISTEN_MAP_EVENTS );
  356. if ( 0 == Q_stricmp( pEventName, GetName() ) )
  357. {
  358. IncrementCount();
  359. }
  360. }
  361. //-----------------------------------------------------------------------------
  362. // Purpose: called when an achievement is awarded
  363. //-----------------------------------------------------------------------------
  364. void CBaseAchievement::AwardAchievement()
  365. {
  366. Assert( !IsAchieved() );
  367. if ( IsAchieved() )
  368. return;
  369. m_pAchievementMgr->AwardAchievement( m_iAchievementID, m_nUserSlot );
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose: called when a component of a multi-component event is found
  373. //-----------------------------------------------------------------------------
  374. void CBaseAchievement::OnComponentEvent( const char *pchComponentName )
  375. {
  376. // find the component name in our list
  377. for ( int i = 0; i < m_iNumComponents; i++ )
  378. {
  379. if ( 0 == Q_strcmp( pchComponentName, m_pszComponentNames[i] ) )
  380. {
  381. EnsureComponentBitSetAndEvaluate( i );
  382. return;
  383. }
  384. }
  385. }
  386. //-----------------------------------------------------------------------------
  387. // Purpose: sets the specified component bit # if it is not already.
  388. // If it does get set, evaluate if this satisfies an achievement
  389. //-----------------------------------------------------------------------------
  390. void CBaseAchievement::EnsureComponentBitSetAndEvaluate( int iBitNumber )
  391. {
  392. Assert( iBitNumber < 64 ); // this is bit #, not a bit mask
  393. if ( IsAchieved() )
  394. return;
  395. // calculate which bit this component corresponds to
  396. uint64 iBitMask = ( (uint64) 1 ) << iBitNumber;
  397. // see if we already have gotten this component
  398. if ( 0 == ( iBitMask & m_iComponentBits ) )
  399. {
  400. if ( !CheckAchievementsEnabled() )
  401. {
  402. Msg( "Achievements disabled, ignoring achievement component for %s\n", GetName() );
  403. return;
  404. }
  405. // new component, set the bit and increment the count
  406. SetComponentBits( m_iComponentBits | iBitMask );
  407. #if 0 // !defined( NO_STEAM ) - <vitaliy> achievements should go via TitleData
  408. // if this achievement's progress should be stored in Steam, set the steam stat for it
  409. if ( StoreProgressInSteam() && steamapicontext->SteamUserStats() )
  410. {
  411. // Set the Steam stat with the same name as the achievement. Only cached locally until we upload it.
  412. char pszProgressName[1024];
  413. Q_snprintf( pszProgressName, 1024, "%s_STAT", GetName() );
  414. bool bRet = steamapicontext->SteamUserStats()->SetStat( pszProgressName, m_iCount );
  415. if ( !bRet )
  416. {
  417. DevMsg( "ISteamUserStats::GetStat failed to set progress value in Steam for achievement %s\n", pszProgressName );
  418. }
  419. if ( HasComponents() )
  420. {
  421. Q_snprintf( pszProgressName, 1024, "%s_COMP", GetName() );
  422. int32 bits = (int32) GetComponentBits();
  423. bool bRet = steamapicontext->SteamUserStats()->SetStat( pszProgressName, bits );
  424. if ( !bRet )
  425. {
  426. DevMsg( "ISteamUserStats::GetStat failed to set component value in Steam for achievement %s\n", pszProgressName );
  427. }
  428. }
  429. // Upload user data to commit the change to Steam so if the client crashes, progress isn't lost.
  430. // Only upload if we haven't uploaded recently, to keep us from spamming Steam with uploads. If we don't
  431. // upload now, it will get uploaded no later than level shutdown.
  432. if ( ( m_pAchievementMgr->GetTimeLastUpload() == 0 ) || ( Plat_FloatTime() - m_pAchievementMgr->GetTimeLastUpload() > 60 * 15 ) )
  433. {
  434. m_pAchievementMgr->UploadUserData( m_nUserSlot );
  435. }
  436. }
  437. #endif
  438. Assert( m_iCount <= m_iGoal );
  439. if ( m_iCount == m_iGoal )
  440. {
  441. // all components found, award the achievement (and save state)
  442. AwardAchievement();
  443. }
  444. else
  445. {
  446. // save our state at the next good opportunity
  447. m_pAchievementMgr->SetDirty( true, m_nUserSlot );
  448. if ( cc_achievement_debug.GetInt() )
  449. {
  450. Msg( "Component %d for achievement %s found\n", iBitNumber, GetName() );
  451. }
  452. ShowProgressNotification();
  453. }
  454. }
  455. else
  456. {
  457. if ( cc_achievement_debug.GetInt() )
  458. {
  459. Msg( "Component %d for achievement %s found, but already had that component\n", iBitNumber, GetName() );
  460. }
  461. }
  462. }
  463. //-----------------------------------------------------------------------------
  464. // Purpose: displays achievement progress notification in the HUD
  465. //-----------------------------------------------------------------------------
  466. void CBaseAchievement::ShowProgressNotification()
  467. {
  468. if ( !ShouldShowProgressNotification() )
  469. return;
  470. IGameEvent *event = gameeventmanager->CreateEvent( "achievement_event" );
  471. if ( event )
  472. {
  473. event->SetString( "achievement_name", GetName() );
  474. event->SetInt( "cur_val", m_iCount );
  475. event->SetInt( "max_val", m_iGoal );
  476. #ifdef GAME_DLL
  477. gameeventmanager->FireEvent( event );
  478. #else
  479. gameeventmanager->FireEventClientSide( event );
  480. #endif
  481. }
  482. }
  483. //-----------------------------------------------------------------------------
  484. // Purpose: clears dynamic state for this achievement
  485. //-----------------------------------------------------------------------------
  486. void CBaseAchievement::PreRestoreSavedGame()
  487. {
  488. // if this achievement gets saved with the game, clear its state
  489. if ( m_iFlags & ACH_SAVE_WITH_GAME )
  490. {
  491. m_iCount = 0;
  492. }
  493. }
  494. //-----------------------------------------------------------------------------
  495. // Purpose: called after the data in this achievement has been restored from saved game
  496. //-----------------------------------------------------------------------------
  497. void CBaseAchievement::PostRestoreSavedGame()
  498. {
  499. EvaluateIsAlreadyAchieved();
  500. }
  501. //-----------------------------------------------------------------------------
  502. // Purpose: sets component bits for this achievement
  503. //-----------------------------------------------------------------------------
  504. void CBaseAchievement::SetComponentBits( uint64 iComponentBits )
  505. {
  506. Assert( m_iFlags & ACH_HAS_COMPONENTS );
  507. // set the bit field
  508. m_iComponentBits = iComponentBits;
  509. // count how many bits are set and save that as the count
  510. int iNumBitsSet = 0;
  511. while ( iComponentBits > 0 )
  512. {
  513. if ( iComponentBits & 1 )
  514. {
  515. iNumBitsSet++;
  516. }
  517. iComponentBits >>= 1;
  518. }
  519. m_iCount = iNumBitsSet;
  520. }
  521. //-----------------------------------------------------------------------------
  522. // Purpose: gets the number of bits that are set in our components
  523. //-----------------------------------------------------------------------------
  524. int CBaseAchievement::GetNumComponentBitsSet( void )
  525. {
  526. int iNumBitsSet = 0;
  527. uint64 iComponentBits = m_iComponentBits;
  528. while ( iComponentBits > 0 )
  529. {
  530. if ( iComponentBits & 1 )
  531. {
  532. iNumBitsSet++;
  533. }
  534. iComponentBits >>= 1;
  535. }
  536. return iNumBitsSet;
  537. }
  538. //-----------------------------------------------------------------------------
  539. // Purpose: returns whether we should save this achievement with a save game
  540. //-----------------------------------------------------------------------------
  541. bool CBaseAchievement::ShouldSaveWithGame()
  542. {
  543. // save if we should get saved with the game, have a non-zero count, and have not
  544. // been achieved (at which point the achievement state gets saved globally)
  545. return ( ( m_iFlags & ACH_SAVE_WITH_GAME ) > 0 && ( GetCount() > 0 ) && !IsAchieved() );
  546. }
  547. //-----------------------------------------------------------------------------
  548. // Purpose: returns whether we should save this achievement to the global file
  549. //-----------------------------------------------------------------------------
  550. bool CBaseAchievement::ShouldSaveGlobal()
  551. {
  552. // save if we should get saved globally and have a non-zero count, or if we have been achieved, or if the player has pinned this achievement to the HUD
  553. return ( ( ( m_iFlags & ACH_SAVE_GLOBAL ) > 0 && ( GetCount() > 0 ) ) || IsAchieved() || ( m_iProgressShown > 0 ) || ShouldShowOnHUD() );
  554. }
  555. //-----------------------------------------------------------------------------
  556. // Purpose: returns whether this achievement is active
  557. //-----------------------------------------------------------------------------
  558. bool CBaseAchievement::IsActive()
  559. {
  560. // we're not active if already achieved
  561. if ( IsAchieved() )
  562. return false;
  563. // if there's a map filter and we're not on the specified map, we're not active
  564. if ( ( m_pMapNameFilter ) && ( 0 != Q_strcmp( m_pAchievementMgr->GetMapName(), m_pMapNameFilter ) ) )
  565. return false;
  566. return true;
  567. }
  568. //-----------------------------------------------------------------------------
  569. // Purpose: returns whether this achievement is available. It can be made
  570. // not available if it requires DLC, etc
  571. //-----------------------------------------------------------------------------
  572. bool CBaseAchievement::IsAvailable()
  573. {
  574. return true; // by default all achievements are available
  575. }
  576. //-----------------------------------------------------------------------------
  577. // Purpose: Serialize our data to the KeyValues node
  578. //-----------------------------------------------------------------------------
  579. void CBaseAchievement::GetSettings( KeyValues *pNodeOut )
  580. {
  581. pNodeOut->SetInt( "value", IsAchieved() ? 1 : 0 );
  582. if ( HasComponents() )
  583. {
  584. pNodeOut->SetUint64( "data", m_iComponentBits );
  585. }
  586. else
  587. {
  588. if ( !IsAchieved() )
  589. {
  590. pNodeOut->SetInt( "data", m_iCount );
  591. }
  592. }
  593. pNodeOut->SetInt( "hud", ShouldShowOnHUD() ? 1 : 0 );
  594. pNodeOut->SetInt( "msg", m_iProgressShown );
  595. }
  596. //-----------------------------------------------------------------------------
  597. // Purpose: Unserialize our data from the KeyValues node
  598. //-----------------------------------------------------------------------------
  599. void CBaseAchievement::ApplySettings( KeyValues *pNodeIn )
  600. {
  601. // set the count
  602. if ( pNodeIn->GetInt( "value" ) > 0 )
  603. {
  604. m_iCount = m_iGoal;
  605. m_bAchieved = true;
  606. }
  607. else if ( !HasComponents() )
  608. {
  609. m_iCount = pNodeIn->GetInt( "data" );
  610. }
  611. // if this achievement has components, set the component bits
  612. if ( HasComponents() )
  613. {
  614. int64 iComponentBits = pNodeIn->GetUint64( "data" );
  615. SetComponentBits( iComponentBits );
  616. }
  617. SetShowOnHUD( !!pNodeIn->GetInt( "hud" ) );
  618. m_iProgressShown = pNodeIn->GetInt( "msg" );
  619. }
  620. //-----------------------------------------------------------------------------
  621. // Purpose: Clears achievement data
  622. //-----------------------------------------------------------------------------
  623. void CBaseAchievement::ClearAchievementData()
  624. {
  625. SetCount( 0 );
  626. if ( this->HasComponents() )
  627. {
  628. this->SetComponentBits( 0 );
  629. }
  630. SetAchieved( false );
  631. }
  632. //-----------------------------------------------------------------------------
  633. // Purpose: get the localizable display string for a specific component
  634. //-----------------------------------------------------------------------------
  635. const char *CBaseAchievement::GetComponentDisplayString( int iComponent )
  636. {
  637. if ( !( m_iFlags & ACH_HAS_COMPONENTS ) || iComponent < 0 || iComponent > m_iNumComponents )
  638. return "";
  639. return m_pszComponentDisplayNames[iComponent];
  640. }
  641. //-----------------------------------------------------------------------------
  642. // Purpose: Translate the asset string into an xbox asset id.
  643. //-----------------------------------------------------------------------------
  644. void CBaseAchievement::SetAssetAward( const char* assetAwardName )
  645. {
  646. TitleAvatarAwardsDescription_t const *pTitleAssetMap = g_pMatchFramework->GetMatchTitle()->DescribeTitleAvatarAwards();
  647. while ( pTitleAssetMap && pTitleAssetMap->m_szAvatarAwardName )
  648. {
  649. if ( !Q_stricmp( pTitleAssetMap->m_szAvatarAwardName, assetAwardName ) )
  650. {
  651. SetAssetAwardID( pTitleAssetMap->m_idAvatarAward );
  652. return;
  653. }
  654. else
  655. {
  656. ++pTitleAssetMap;
  657. }
  658. }
  659. SetAssetAwardID( 0 );
  660. }
  661. //-----------------------------------------------------------------------------
  662. // Purpose: Wrapper for achievement manager so we can check enabled per achievement with the gamerules
  663. //-----------------------------------------------------------------------------
  664. bool CBaseAchievement::CheckAchievementsEnabled( void )
  665. {
  666. if ( g_pGameRules && !g_pGameRules->CheckAchievementsEnabled( GetAchievementID() ) )
  667. return false;
  668. return m_pAchievementMgr->CheckAchievementsEnabled();
  669. }
  670. //-----------------------------------------------------------------------------
  671. // Purpose: Constructor
  672. //-----------------------------------------------------------------------------
  673. CFailableAchievement::CFailableAchievement() : CBaseAchievement()
  674. {
  675. m_bFailed = false;
  676. m_bActivated = false;
  677. }
  678. //-----------------------------------------------------------------------------
  679. // Purpose: returns whether we should save this achievement with a save game
  680. //-----------------------------------------------------------------------------
  681. bool CFailableAchievement::ShouldSaveWithGame()
  682. {
  683. // save if we should get saved with the game, and are active or have failed
  684. return ( ( ( m_iFlags & ACH_SAVE_WITH_GAME ) > 0 ) && ( m_bActivated || m_bFailed ) );
  685. }
  686. //-----------------------------------------------------------------------------
  687. // Purpose: clears dynamic state for this achievement
  688. //-----------------------------------------------------------------------------
  689. void CFailableAchievement::PreRestoreSavedGame()
  690. {
  691. m_bFailed = false;
  692. m_bActivated = false;
  693. BaseClass::PreRestoreSavedGame();
  694. }
  695. //-----------------------------------------------------------------------------
  696. // Purpose: called after the data in this achievement has been restored from saved game
  697. //-----------------------------------------------------------------------------
  698. void CFailableAchievement::PostRestoreSavedGame()
  699. {
  700. // if there is no activation event set for this achievement, it is always active, activate it now
  701. if ( !m_bFailed && !GetActivationEventName()[0] )
  702. {
  703. m_bActivated = true;
  704. }
  705. if ( m_bActivated )
  706. {
  707. Activate();
  708. }
  709. BaseClass::PostRestoreSavedGame();
  710. }
  711. //-----------------------------------------------------------------------------
  712. // Purpose: called when a map event occurs
  713. //-----------------------------------------------------------------------------
  714. void CFailableAchievement::OnMapEvent( const char *pEventName )
  715. {
  716. // if we're not activated and we got the activation event, activate
  717. if ( !m_bActivated && ( 0 == Q_stricmp( pEventName, GetActivationEventName() ) ) )
  718. {
  719. OnActivationEvent();
  720. }
  721. // if this is the evaluation event, see if we've failed or not
  722. else if ( m_bActivated && 0 == Q_stricmp( pEventName, GetEvaluationEventName() ) )
  723. {
  724. OnEvaluationEvent();
  725. }
  726. }
  727. //-----------------------------------------------------------------------------
  728. // Purpose: Called when this failable achievement is activated
  729. //-----------------------------------------------------------------------------
  730. void CFailableAchievement::Activate()
  731. {
  732. m_bActivated = true;
  733. ListenForEvents();
  734. if ( cc_achievement_debug.GetInt() )
  735. {
  736. Msg( "Failable achievement %s now active\n", GetName() );
  737. }
  738. }
  739. //-----------------------------------------------------------------------------
  740. // Purpose: Called when this failable achievement should be evaluated
  741. //-----------------------------------------------------------------------------
  742. void CFailableAchievement::OnEvaluationEvent()
  743. {
  744. if ( !m_bFailed )
  745. {
  746. // we haven't failed and we reached the evaluation point, we've succeeded
  747. IncrementCount();
  748. }
  749. if ( cc_achievement_debug.GetInt() )
  750. {
  751. Msg( "Failable achievement %s has been evaluated (%s), now inactive\n", GetName(), m_bFailed ? "FAILED" : "AWARDED" );
  752. }
  753. }
  754. //-----------------------------------------------------------------------------
  755. // Purpose: Sets this achievement to failed
  756. //-----------------------------------------------------------------------------
  757. void CFailableAchievement::SetFailed()
  758. {
  759. if ( !m_bFailed )
  760. {
  761. m_bFailed = true;
  762. if ( cc_achievement_debug.GetInt() )
  763. {
  764. Msg( "Achievement failed: %s (%s)\n", GetName(), GetName() );
  765. }
  766. }
  767. }
  768. //===========================================
  769. void CAchievement_AchievedCount::Init()
  770. {
  771. SetFlags( ACH_SAVE_GLOBAL );
  772. SetGoal( 1 );
  773. SetAchievementsRequired( 0, 0, 0 );
  774. }
  775. // Count how many achievements have been earned in our range
  776. void CAchievement_AchievedCount::OnSteamUserStatsStored( void )
  777. {
  778. // DO NO CALL. REPLACED BY CHECKMETAACHIEVEMENTS!
  779. Assert( 0 );
  780. }
  781. void CAchievement_AchievedCount::SetAchievementsRequired( int iNumRequired, int iLowRange, int iHighRange )
  782. {
  783. m_iNumRequired = iNumRequired;
  784. m_iLowRange = iLowRange;
  785. m_iHighRange = iHighRange;
  786. }