Team Fortress 2 Source Code as on 22/4/2020
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.

750 lines
24 KiB

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