aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/shared/baseachievement.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/shared/baseachievement.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/shared/baseachievement.cpp')
-rw-r--r--mp/src/game/shared/baseachievement.cpp1494
1 files changed, 747 insertions, 747 deletions
diff --git a/mp/src/game/shared/baseachievement.cpp b/mp/src/game/shared/baseachievement.cpp
index 82db3c32..494133c1 100644
--- a/mp/src/game/shared/baseachievement.cpp
+++ b/mp/src/game/shared/baseachievement.cpp
@@ -1,748 +1,748 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================
-
-#include "cbase.h"
-#include "achievementmgr.h"
-#include "icommandline.h"
-#ifdef CLIENT_DLL
-#include "tier3/tier3.h"
-#include "vgui/ILocalize.h"
-#include "achievement_notification_panel.h"
-#include "fmtstr.h"
-#include "gamestats.h"
-#endif // CLIENT_DLL
-
-//=============================================================================
-// HPE_BEGIN
-// [dwenger] Necessary for sorting achievements by award time
-//=============================================================================
-
-#include <vgui/ISystem.h>
-#include "vgui_controls/Controls.h"
-
-//=============================================================================
-// HPE_END
-//=============================================================================
-
-CBaseAchievementHelper *CBaseAchievementHelper::s_pFirst = NULL;
-
-BEGIN_DATADESC_NO_BASE( CBaseAchievement )
-DEFINE_FIELD( m_iCount, FIELD_INTEGER ),
-END_DATADESC()
-
-BEGIN_DATADESC( CFailableAchievement )
-DEFINE_FIELD( m_bActivated, FIELD_BOOLEAN ),
-DEFINE_FIELD( m_bFailed, FIELD_BOOLEAN ),
-END_DATADESC()
-
-//-----------------------------------------------------------------------------
-// Purpose: constructor
-//-----------------------------------------------------------------------------
-CBaseAchievement::CBaseAchievement()
-{
- m_iFlags = 0;
- m_iGoal = 0;
- m_iProgressMsgIncrement = 0;
- m_iProgressMsgMinimum = 0;
- m_iAchievementID = 0;
- m_iPointValue = 0;
- m_bHideUntilAchieved = false;
- m_bStoreProgressInSteam = false;
- m_pVictimClassNameFilter = NULL;
- m_pAttackerClassNameFilter = NULL;
- m_pInflictorClassNameFilter = NULL;
- m_pInflictorEntityNameFilter = NULL;
- m_pMapNameFilter = NULL;
- m_pGameDirFilter = NULL;
- m_pszComponentNames = NULL;
- m_pszComponentPrefix = NULL;
- m_iNumComponents = 0;
- m_iComponentPrefixLen = 0;
- m_iComponentBits = 0;
- m_iCount = 0;
- m_iProgressShown = 0;
- m_bAchieved = false;
- m_uUnlockTime = 0;
- m_pAchievementMgr = NULL;
- m_bShowOnHUD = false;
- m_pszStat = NULL;
-}
-
-CBaseAchievement::~CBaseAchievement()
-{
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets flags
-//-----------------------------------------------------------------------------
-void CBaseAchievement::SetFlags( int iFlags )
-{
- // must always specify a save method
- Assert( iFlags & ( ACH_SAVE_WITH_GAME | ACH_SAVE_GLOBAL ) );
-
- m_iFlags = iFlags;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: called when a game event being listened for is dispatched
-//-----------------------------------------------------------------------------
-void CBaseAchievement::FireGameEvent( IGameEvent *event )
-{
- // perform common filtering to make it simpler to write achievements
- if ( !IsActive() )
- return;
-
- // if the achievement only applies to a specific map, and it's not the current map, skip it
- if ( m_pMapNameFilter && ( 0 != Q_strcmp( m_pAchievementMgr->GetMapName(), m_pMapNameFilter ) ) )
- return;
-
- const char *name = event->GetName();
- if ( 0 == Q_strcmp( name, "teamplay_round_win" ) )
- {
- // if this is a round win and the achievement wants full round events only, filter this out
- // if this is not the end of a full round
- if ( ( m_iFlags & ACH_FILTER_FULL_ROUND_ONLY ) && ( false == event->GetBool( "full_round" ) ) )
- return;
- }
-
- // let the achievement handle the event
- FireGameEvent_Internal( event );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: sets victim class to filter with
-//-----------------------------------------------------------------------------
-void CBaseAchievement::SetVictimFilter( const char *pClassName )
-{
- m_pVictimClassNameFilter = pClassName;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets attacker class to filter with
-//-----------------------------------------------------------------------------
-void CBaseAchievement::SetAttackerFilter( const char *pClassName )
-{
- m_pAttackerClassNameFilter = pClassName;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets inflictor class to filter with
-//-----------------------------------------------------------------------------
-void CBaseAchievement::SetInflictorFilter( const char *pClassName )
-{
- m_pInflictorClassNameFilter = pClassName;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets inflictor entity name to filter with
-//-----------------------------------------------------------------------------
-void CBaseAchievement::SetInflictorEntityNameFilter( const char *pEntityName )
-{
- m_pInflictorEntityNameFilter = pEntityName;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets map name to filter with
-//-----------------------------------------------------------------------------
-void CBaseAchievement::SetMapNameFilter( const char *pMapName )
-{
- m_pMapNameFilter = pMapName;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets game dir to filter with. Note: in general, achievements should
-// only be compiled into products they pertain to. But if there are
-// any game-specific achievements which need to be in a binary shared
-// across products (e.g. Ep1 & Ep2), use the game dir as a runtime
-// filter.
-//-----------------------------------------------------------------------------
-void CBaseAchievement::SetGameDirFilter( const char *pGameDir )
-{
- m_pGameDirFilter = pGameDir;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets prefix to look for in map event string to identify a component
-// for this achievement
-//-----------------------------------------------------------------------------
-void CBaseAchievement::SetComponentPrefix( const char *pPrefix )
-{
- m_pszComponentPrefix = pPrefix;
- m_iComponentPrefixLen = Q_strlen( pPrefix );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: called when a kill that passes filter critera occurs. This
-// is the default implementation, achievements can override to
-// do special handling
-//-----------------------------------------------------------------------------
-void CBaseAchievement::Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event )
-{
- // extra paranoid check: should only get here if registered as a kill event listener
- Assert( GetFlags() & ACH_LISTEN_KILL_EVENTS );
- if ( !( GetFlags() & ACH_LISTEN_KILL_EVENTS ) )
- return;
-
- // default implementation is just to increase count when filter criteria pass
- // Msg( "Base achievement incremented on kill event.\n" );
- IncrementCount();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: called when an event that counts toward an achievement occurs
-//-----------------------------------------------------------------------------
-void CBaseAchievement::IncrementCount( int iOptIncrement )
-{
- if ( !IsAchieved() && LocalPlayerCanEarn() )
- {
- if ( !AlwaysEnabled() && !m_pAchievementMgr->CheckAchievementsEnabled() )
- {
- Msg( "Achievements disabled, ignoring achievement progress for %s\n", GetName() );
- return;
- }
-
- // on client, where the count is kept, increment count
- if ( iOptIncrement > 0 )
- {
- // user specified that we want to increase by more than one.
- m_iCount += iOptIncrement;
- if ( m_iCount > m_iGoal )
- {
- m_iCount = m_iGoal;
- }
- }
- else
- {
- m_iCount++;
- }
-
- // if this achievement gets saved w/global state, flag our global state as dirty
- if ( GetFlags() & ACH_SAVE_GLOBAL )
- {
- m_pAchievementMgr->SetDirty( true );
- }
-
- if ( cc_achievement_debug.GetInt() )
- {
- Msg( "Achievement count increased for %s: %d/%d\n", GetName(), m_iCount, m_iGoal );
- }
-
-#ifndef NO_STEAM
- // if this achievement's progress should be stored in Steam, set the steam stat for it
- if ( StoreProgressInSteam() && steamapicontext->SteamUserStats() )
- {
- // Set the Steam stat with the same name as the achievement. Only cached locally until we upload it.
- char pszProgressName[1024];
- Q_snprintf( pszProgressName, 1024, "%s_STAT", GetStat() );
- bool bRet = steamapicontext->SteamUserStats()->SetStat( pszProgressName, m_iCount );
- if ( !bRet )
- {
- DevMsg( "ISteamUserStats::GetStat failed to set progress value in Steam for achievement %s\n", pszProgressName );
- }
-
- m_pAchievementMgr->SetDirty( true );
- }
-#endif
- // if we've hit goal, award the achievement
- if ( m_iGoal > 0 )
- {
- if ( m_iCount >= m_iGoal )
- {
- AwardAchievement();
- }
- else
- {
- HandleProgressUpdate();
- }
- }
-
- }
-}
-
-void CBaseAchievement::SetShowOnHUD( bool bShow )
-{
- if ( m_bShowOnHUD != bShow )
- {
- m_pAchievementMgr->SetDirty( true );
- }
-
- m_bShowOnHUD = bShow;
-}
-
-void CBaseAchievement::HandleProgressUpdate()
-{
- // if we've hit the right # of progress steps to show a progress notification, show it
- if ( ( m_iProgressMsgIncrement > 0 ) && m_iCount >= m_iProgressMsgMinimum && ( 0 == ( m_iCount % m_iProgressMsgIncrement ) ) )
- {
- // which notification is this
- int iProgress = m_iCount / m_iProgressMsgIncrement;
- // if we haven't already shown this progress step, show it
- if ( iProgress > m_iProgressShown )
- {
- ShowProgressNotification();
- // remember progress step shown so we don't show it again if the player loads an earlier save game
- // and gets past this point again
- m_iProgressShown = iProgress;
- m_pAchievementMgr->SetDirty( true );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: calculates at how many steps we should show a progress notification
-//-----------------------------------------------------------------------------
-void CBaseAchievement::CalcProgressMsgIncrement()
-{
- // by default, show progress at every 25%
- m_iProgressMsgIncrement = m_iGoal / 4;
- // if goal is not evenly divisible by 4, try some other values
- if ( 0 != ( m_iGoal % 4 ) )
- {
- if ( 0 == ( m_iGoal % 3 ) )
- {
- // if evenly divisible by 3, use that
- m_iProgressMsgIncrement = m_iGoal / 3;
- }
- else if ( 0 == ( m_iGoal % 5 ) )
- {
- // if evenly divisible by 5, use that
- m_iProgressMsgIncrement = m_iGoal / 5;
- }
- // otherwise stick with divided by 4, rounded off
- }
-
- // don't show progress notifications for less than 5 things
- if ( m_iProgressMsgIncrement < 5 )
- {
- m_iProgressMsgIncrement = 0;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBaseAchievement::SetNextThink( float flThinkTime )
-{
- m_pAchievementMgr->SetAchievementThink( this, flThinkTime );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBaseAchievement::ClearThink( void )
-{
- m_pAchievementMgr->SetAchievementThink( this, THINK_CLEAR );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: see if we should award an achievement based on what just happened
-//-----------------------------------------------------------------------------
-void CBaseAchievement::EvaluateNewAchievement()
-{
- if ( !IsAchieved() && m_iGoal > 0 && m_iCount >= m_iGoal )
- {
- AwardAchievement();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: determine if we should set this achievement to be achieved based
-// on other state. Used at init time.
-//-----------------------------------------------------------------------------
-void CBaseAchievement::EvaluateIsAlreadyAchieved()
-{
- if ( !IsAchieved() && m_iGoal > 0 && m_iCount >= m_iGoal )
- {
- m_bAchieved = true;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: called a map event for this achievement occurs
-//-----------------------------------------------------------------------------
-void CBaseAchievement::OnMapEvent( const char *pEventName )
-{
- Assert( m_iFlags & ACH_LISTEN_MAP_EVENTS );
-
- if ( 0 == Q_stricmp( pEventName, GetName() ) )
- {
- IncrementCount();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: called when an achievement is awarded
-//-----------------------------------------------------------------------------
-void CBaseAchievement::AwardAchievement()
-{
- Assert( !IsAchieved() );
- if ( IsAchieved() )
- return;
-
- m_pAchievementMgr->AwardAchievement( m_iAchievementID );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: called when a component of a multi-component event is found
-//-----------------------------------------------------------------------------
-void CBaseAchievement::OnComponentEvent( const char *pchComponentName )
-{
- // find the component name in our list
- for ( int i = 0; i < m_iNumComponents; i++ )
- {
- if ( 0 == Q_strcmp( pchComponentName, m_pszComponentNames[i] ) )
- {
- EnsureComponentBitSetAndEvaluate( i );
- return;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets the specified component bit # if it is not already.
-// If it does get set, evaluate if this satisfies an achievement
-//-----------------------------------------------------------------------------
-void CBaseAchievement::EnsureComponentBitSetAndEvaluate( int iBitNumber )
-{
- Assert( iBitNumber < 64 ); // this is bit #, not a bit mask
-
- if ( IsAchieved() )
- return;
-
- // calculate which bit this component corresponds to
- uint64 iBitMask = ( (uint64) 1 ) << iBitNumber;
-
- // see if we already have gotten this component
- if ( 0 == ( iBitMask & m_iComponentBits ) )
- {
- if ( !AlwaysEnabled() && !m_pAchievementMgr->CheckAchievementsEnabled() )
- {
- Msg( "Achievements disabled, ignoring achievement component for %s\n", GetName() );
- return;
- }
-
- // new component, set the bit and increment the count
- SetComponentBits( m_iComponentBits | iBitMask );
- Assert( m_iCount <= m_iGoal );
- if ( m_iCount == m_iGoal )
- {
- // all components found, award the achievement (and save state)
- AwardAchievement();
- }
- else
- {
- // save our state at the next good opportunity
- m_pAchievementMgr->SetDirty( true );
-
- if ( cc_achievement_debug.GetInt() )
- {
- Msg( "Component %d for achievement %s found\n", iBitNumber, GetName() );
- }
-
- ShowProgressNotification();
- }
- }
- else
- {
- if ( cc_achievement_debug.GetInt() )
- {
- Msg( "Component %d for achievement %s found, but already had that component\n", iBitNumber, GetName() );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: displays achievement progress notification in the HUD
-//-----------------------------------------------------------------------------
-void CBaseAchievement::ShowProgressNotification()
-{
- if ( !ShouldShowProgressNotification() )
- return;
-
- IGameEvent *event = gameeventmanager->CreateEvent( "achievement_event" );
- if ( event )
- {
- event->SetString( "achievement_name", GetName() );
- event->SetInt( "cur_val", m_iCount );
- event->SetInt( "max_val", m_iGoal );
-#ifdef GAME_DLL
- gameeventmanager->FireEvent( event );
-#else
- gameeventmanager->FireEventClientSide( event );
-#endif
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: clears dynamic state for this achievement
-//-----------------------------------------------------------------------------
-void CBaseAchievement::PreRestoreSavedGame()
-{
- // if this achievement gets saved with the game, clear its state
- if ( m_iFlags & ACH_SAVE_WITH_GAME )
- {
- m_iCount = 0;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: called after the data in this achievement has been restored from saved game
-//-----------------------------------------------------------------------------
-void CBaseAchievement::PostRestoreSavedGame()
-{
- EvaluateIsAlreadyAchieved();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: sets component bits for this achievement
-//-----------------------------------------------------------------------------
-void CBaseAchievement::SetComponentBits( uint64 iComponentBits )
-{
- Assert( m_iFlags & ACH_HAS_COMPONENTS );
- // set the bit field
- m_iComponentBits = iComponentBits;
- // count how many bits are set and save that as the count
- int iNumBitsSet = 0;
- while ( iComponentBits > 0 )
- {
- if ( iComponentBits & 1 )
- {
- iNumBitsSet++;
- }
- iComponentBits >>= 1;
- }
- m_iCount = iNumBitsSet;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: returns whether we should save this achievement with a save game
-//-----------------------------------------------------------------------------
-bool CBaseAchievement::ShouldSaveWithGame()
-{
- // save if we should get saved with the game, have a non-zero count, and have not
- // been achieved (at which point the achievement state gets saved globally)
- return ( ( m_iFlags & ACH_SAVE_WITH_GAME ) > 0 && ( GetCount() > 0 ) && !IsAchieved() );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: returns whether we should save this achievement to the global file
-//-----------------------------------------------------------------------------
-bool CBaseAchievement::ShouldSaveGlobal()
-{
- // 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
- return ( ( ( m_iFlags & ACH_SAVE_GLOBAL ) > 0 && ( GetCount() > 0 ) ) || IsAchieved() || ( m_iProgressShown > 0 ) || ShouldShowOnHUD() );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: returns whether this achievement is active
-//-----------------------------------------------------------------------------
-bool CBaseAchievement::IsActive()
-{
- // we're not active if already achieved
- if ( IsAchieved() )
- return false;
-
- // if there's a map filter and we're not on the specified map, we're not active
- if ( ( m_pMapNameFilter ) && ( 0 != Q_strcmp( m_pAchievementMgr->GetMapName(), m_pMapNameFilter ) ) )
- return false;
-
- return true;
-}
-
-//=============================================================================
-// HPE_BEGIN:
-// [pfreese] Moved serialization from AchievementMgr to here so that derived
-// classes can persist additional data
-//=============================================================================
-
-//-----------------------------------------------------------------------------
-// Purpose: Serialize our data to the KeyValues node
-//-----------------------------------------------------------------------------
-void CBaseAchievement::GetSettings( KeyValues *pNodeOut )
-{
- pNodeOut->SetInt( "value", IsAchieved() ? 1 : 0 );
-
- if ( HasComponents() )
- {
- pNodeOut->SetUint64( "data", m_iComponentBits );
- }
- else
- {
- if ( !IsAchieved() )
- {
- pNodeOut->SetInt( "data", m_iCount );
- }
- }
- pNodeOut->SetInt( "hud", ShouldShowOnHUD() ? 1 : 0 );
- pNodeOut->SetInt( "msg", m_iProgressShown );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Unserialize our data from the KeyValues node
-//-----------------------------------------------------------------------------
-void CBaseAchievement::ApplySettings( KeyValues *pNodeIn )
-{
- // set the count
- if ( pNodeIn->GetInt( "value" ) > 0 )
- {
- m_iCount = m_iGoal;
- m_bAchieved = true;
- }
- else if ( !HasComponents() )
- {
- m_iCount = pNodeIn->GetInt( "data" );
- }
-
- // if this achievement has components, set the component bits
- if ( HasComponents() )
- {
- int64 iComponentBits = pNodeIn->GetUint64( "data" );
- SetComponentBits( iComponentBits );
- }
- SetShowOnHUD( !!pNodeIn->GetInt( "hud" ) );
- m_iProgressShown = pNodeIn->GetInt( "msg" );
-}
-
-//=============================================================================
-// HPE_END
-//=============================================================================
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-//-----------------------------------------------------------------------------
-CFailableAchievement::CFailableAchievement() : CBaseAchievement()
-{
- m_bFailed = false;
- m_bActivated = false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: returns whether we should save this achievement with a save game
-//-----------------------------------------------------------------------------
-bool CFailableAchievement::ShouldSaveWithGame()
-{
- // save if we should get saved with the game, and are active or have failed
- return ( ( ( m_iFlags & ACH_SAVE_WITH_GAME ) > 0 ) && ( m_bActivated || m_bFailed ) );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: clears dynamic state for this achievement
-//-----------------------------------------------------------------------------
-void CFailableAchievement::PreRestoreSavedGame()
-{
- m_bFailed = false;
- m_bActivated = false;
-
- BaseClass::PreRestoreSavedGame();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: called after the data in this achievement has been restored from saved game
-//-----------------------------------------------------------------------------
-void CFailableAchievement::PostRestoreSavedGame()
-{
- // if there is no activation event set for this achievement, it is always active, activate it now
- if ( !m_bFailed && !GetActivationEventName()[0] )
- {
- m_bActivated = true;
- }
-
- if ( m_bActivated )
- {
- Activate();
- }
-
- BaseClass::PostRestoreSavedGame();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: called when a map event occurs
-//-----------------------------------------------------------------------------
-void CFailableAchievement::OnMapEvent( const char *pEventName )
-{
- // if we're not activated and we got the activation event, activate
- if ( !m_bActivated && ( 0 == Q_stricmp( pEventName, GetActivationEventName() ) ) )
- {
- OnActivationEvent();
- }
- // if this is the evaluation event, see if we've failed or not
- else if ( m_bActivated && 0 == Q_stricmp( pEventName, GetEvaluationEventName() ) )
- {
- OnEvaluationEvent();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Called when this failable achievement is activated
-//-----------------------------------------------------------------------------
-void CFailableAchievement::Activate()
-{
- m_bActivated = true;
- ListenForEvents();
- if ( cc_achievement_debug.GetInt() )
- {
- Msg( "Failable achievement %s now active\n", GetName() );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Called when this failable achievement should be evaluated
-//-----------------------------------------------------------------------------
-void CFailableAchievement::OnEvaluationEvent()
-{
- if ( !m_bFailed )
- {
- // we haven't failed and we reached the evaluation point, we've succeeded
- IncrementCount();
- }
-
- if ( cc_achievement_debug.GetInt() )
- {
- Msg( "Failable achievement %s has been evaluated (%s), now inactive\n", GetName(), m_bFailed ? "FAILED" : "AWARDED" );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Sets this achievement to failed
-//-----------------------------------------------------------------------------
-void CFailableAchievement::SetFailed()
-{
- if ( !m_bFailed )
- {
- m_bFailed = true;
-
- if ( cc_achievement_debug.GetInt() )
- {
- Msg( "Achievement failed: %s (%s)\n", GetName(), GetName() );
- }
- }
-}
-
-//===========================================
-
-void CAchievement_AchievedCount::Init()
-{
- SetFlags( ACH_SAVE_GLOBAL );
- SetGoal( 1 );
- SetAchievementsRequired( 0, 0, 0 );
-}
-
-// Count how many achievements have been earned in our range
-void CAchievement_AchievedCount::OnSteamUserStatsStored( void )
-{
- // DO NO CALL. REPLACED BY CHECKMETAACHIEVEMENTS!
- Assert( 0 );
-}
-
-void CAchievement_AchievedCount::SetAchievementsRequired( int iNumRequired, int iLowRange, int iHighRange )
-{
- m_iNumRequired = iNumRequired;
- m_iLowRange = iLowRange;
- m_iHighRange = iHighRange;
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "achievementmgr.h"
+#include "icommandline.h"
+#ifdef CLIENT_DLL
+#include "tier3/tier3.h"
+#include "vgui/ILocalize.h"
+#include "achievement_notification_panel.h"
+#include "fmtstr.h"
+#include "gamestats.h"
+#endif // CLIENT_DLL
+
+//=============================================================================
+// HPE_BEGIN
+// [dwenger] Necessary for sorting achievements by award time
+//=============================================================================
+
+#include <vgui/ISystem.h>
+#include "vgui_controls/Controls.h"
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+CBaseAchievementHelper *CBaseAchievementHelper::s_pFirst = NULL;
+
+BEGIN_DATADESC_NO_BASE( CBaseAchievement )
+DEFINE_FIELD( m_iCount, FIELD_INTEGER ),
+END_DATADESC()
+
+BEGIN_DATADESC( CFailableAchievement )
+DEFINE_FIELD( m_bActivated, FIELD_BOOLEAN ),
+DEFINE_FIELD( m_bFailed, FIELD_BOOLEAN ),
+END_DATADESC()
+
+//-----------------------------------------------------------------------------
+// Purpose: constructor
+//-----------------------------------------------------------------------------
+CBaseAchievement::CBaseAchievement()
+{
+ m_iFlags = 0;
+ m_iGoal = 0;
+ m_iProgressMsgIncrement = 0;
+ m_iProgressMsgMinimum = 0;
+ m_iAchievementID = 0;
+ m_iPointValue = 0;
+ m_bHideUntilAchieved = false;
+ m_bStoreProgressInSteam = false;
+ m_pVictimClassNameFilter = NULL;
+ m_pAttackerClassNameFilter = NULL;
+ m_pInflictorClassNameFilter = NULL;
+ m_pInflictorEntityNameFilter = NULL;
+ m_pMapNameFilter = NULL;
+ m_pGameDirFilter = NULL;
+ m_pszComponentNames = NULL;
+ m_pszComponentPrefix = NULL;
+ m_iNumComponents = 0;
+ m_iComponentPrefixLen = 0;
+ m_iComponentBits = 0;
+ m_iCount = 0;
+ m_iProgressShown = 0;
+ m_bAchieved = false;
+ m_uUnlockTime = 0;
+ m_pAchievementMgr = NULL;
+ m_bShowOnHUD = false;
+ m_pszStat = NULL;
+}
+
+CBaseAchievement::~CBaseAchievement()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets flags
+//-----------------------------------------------------------------------------
+void CBaseAchievement::SetFlags( int iFlags )
+{
+ // must always specify a save method
+ Assert( iFlags & ( ACH_SAVE_WITH_GAME | ACH_SAVE_GLOBAL ) );
+
+ m_iFlags = iFlags;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when a game event being listened for is dispatched
+//-----------------------------------------------------------------------------
+void CBaseAchievement::FireGameEvent( IGameEvent *event )
+{
+ // perform common filtering to make it simpler to write achievements
+ if ( !IsActive() )
+ return;
+
+ // if the achievement only applies to a specific map, and it's not the current map, skip it
+ if ( m_pMapNameFilter && ( 0 != Q_strcmp( m_pAchievementMgr->GetMapName(), m_pMapNameFilter ) ) )
+ return;
+
+ const char *name = event->GetName();
+ if ( 0 == Q_strcmp( name, "teamplay_round_win" ) )
+ {
+ // if this is a round win and the achievement wants full round events only, filter this out
+ // if this is not the end of a full round
+ if ( ( m_iFlags & ACH_FILTER_FULL_ROUND_ONLY ) && ( false == event->GetBool( "full_round" ) ) )
+ return;
+ }
+
+ // let the achievement handle the event
+ FireGameEvent_Internal( event );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: sets victim class to filter with
+//-----------------------------------------------------------------------------
+void CBaseAchievement::SetVictimFilter( const char *pClassName )
+{
+ m_pVictimClassNameFilter = pClassName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets attacker class to filter with
+//-----------------------------------------------------------------------------
+void CBaseAchievement::SetAttackerFilter( const char *pClassName )
+{
+ m_pAttackerClassNameFilter = pClassName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets inflictor class to filter with
+//-----------------------------------------------------------------------------
+void CBaseAchievement::SetInflictorFilter( const char *pClassName )
+{
+ m_pInflictorClassNameFilter = pClassName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets inflictor entity name to filter with
+//-----------------------------------------------------------------------------
+void CBaseAchievement::SetInflictorEntityNameFilter( const char *pEntityName )
+{
+ m_pInflictorEntityNameFilter = pEntityName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets map name to filter with
+//-----------------------------------------------------------------------------
+void CBaseAchievement::SetMapNameFilter( const char *pMapName )
+{
+ m_pMapNameFilter = pMapName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets game dir to filter with. Note: in general, achievements should
+// only be compiled into products they pertain to. But if there are
+// any game-specific achievements which need to be in a binary shared
+// across products (e.g. Ep1 & Ep2), use the game dir as a runtime
+// filter.
+//-----------------------------------------------------------------------------
+void CBaseAchievement::SetGameDirFilter( const char *pGameDir )
+{
+ m_pGameDirFilter = pGameDir;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets prefix to look for in map event string to identify a component
+// for this achievement
+//-----------------------------------------------------------------------------
+void CBaseAchievement::SetComponentPrefix( const char *pPrefix )
+{
+ m_pszComponentPrefix = pPrefix;
+ m_iComponentPrefixLen = Q_strlen( pPrefix );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when a kill that passes filter critera occurs. This
+// is the default implementation, achievements can override to
+// do special handling
+//-----------------------------------------------------------------------------
+void CBaseAchievement::Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event )
+{
+ // extra paranoid check: should only get here if registered as a kill event listener
+ Assert( GetFlags() & ACH_LISTEN_KILL_EVENTS );
+ if ( !( GetFlags() & ACH_LISTEN_KILL_EVENTS ) )
+ return;
+
+ // default implementation is just to increase count when filter criteria pass
+ // Msg( "Base achievement incremented on kill event.\n" );
+ IncrementCount();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when an event that counts toward an achievement occurs
+//-----------------------------------------------------------------------------
+void CBaseAchievement::IncrementCount( int iOptIncrement )
+{
+ if ( !IsAchieved() && LocalPlayerCanEarn() )
+ {
+ if ( !AlwaysEnabled() && !m_pAchievementMgr->CheckAchievementsEnabled() )
+ {
+ Msg( "Achievements disabled, ignoring achievement progress for %s\n", GetName() );
+ return;
+ }
+
+ // on client, where the count is kept, increment count
+ if ( iOptIncrement > 0 )
+ {
+ // user specified that we want to increase by more than one.
+ m_iCount += iOptIncrement;
+ if ( m_iCount > m_iGoal )
+ {
+ m_iCount = m_iGoal;
+ }
+ }
+ else
+ {
+ m_iCount++;
+ }
+
+ // if this achievement gets saved w/global state, flag our global state as dirty
+ if ( GetFlags() & ACH_SAVE_GLOBAL )
+ {
+ m_pAchievementMgr->SetDirty( true );
+ }
+
+ if ( cc_achievement_debug.GetInt() )
+ {
+ Msg( "Achievement count increased for %s: %d/%d\n", GetName(), m_iCount, m_iGoal );
+ }
+
+#ifndef NO_STEAM
+ // if this achievement's progress should be stored in Steam, set the steam stat for it
+ if ( StoreProgressInSteam() && steamapicontext->SteamUserStats() )
+ {
+ // Set the Steam stat with the same name as the achievement. Only cached locally until we upload it.
+ char pszProgressName[1024];
+ Q_snprintf( pszProgressName, 1024, "%s_STAT", GetStat() );
+ bool bRet = steamapicontext->SteamUserStats()->SetStat( pszProgressName, m_iCount );
+ if ( !bRet )
+ {
+ DevMsg( "ISteamUserStats::GetStat failed to set progress value in Steam for achievement %s\n", pszProgressName );
+ }
+
+ m_pAchievementMgr->SetDirty( true );
+ }
+#endif
+ // if we've hit goal, award the achievement
+ if ( m_iGoal > 0 )
+ {
+ if ( m_iCount >= m_iGoal )
+ {
+ AwardAchievement();
+ }
+ else
+ {
+ HandleProgressUpdate();
+ }
+ }
+
+ }
+}
+
+void CBaseAchievement::SetShowOnHUD( bool bShow )
+{
+ if ( m_bShowOnHUD != bShow )
+ {
+ m_pAchievementMgr->SetDirty( true );
+ }
+
+ m_bShowOnHUD = bShow;
+}
+
+void CBaseAchievement::HandleProgressUpdate()
+{
+ // if we've hit the right # of progress steps to show a progress notification, show it
+ if ( ( m_iProgressMsgIncrement > 0 ) && m_iCount >= m_iProgressMsgMinimum && ( 0 == ( m_iCount % m_iProgressMsgIncrement ) ) )
+ {
+ // which notification is this
+ int iProgress = m_iCount / m_iProgressMsgIncrement;
+ // if we haven't already shown this progress step, show it
+ if ( iProgress > m_iProgressShown )
+ {
+ ShowProgressNotification();
+ // remember progress step shown so we don't show it again if the player loads an earlier save game
+ // and gets past this point again
+ m_iProgressShown = iProgress;
+ m_pAchievementMgr->SetDirty( true );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: calculates at how many steps we should show a progress notification
+//-----------------------------------------------------------------------------
+void CBaseAchievement::CalcProgressMsgIncrement()
+{
+ // by default, show progress at every 25%
+ m_iProgressMsgIncrement = m_iGoal / 4;
+ // if goal is not evenly divisible by 4, try some other values
+ if ( 0 != ( m_iGoal % 4 ) )
+ {
+ if ( 0 == ( m_iGoal % 3 ) )
+ {
+ // if evenly divisible by 3, use that
+ m_iProgressMsgIncrement = m_iGoal / 3;
+ }
+ else if ( 0 == ( m_iGoal % 5 ) )
+ {
+ // if evenly divisible by 5, use that
+ m_iProgressMsgIncrement = m_iGoal / 5;
+ }
+ // otherwise stick with divided by 4, rounded off
+ }
+
+ // don't show progress notifications for less than 5 things
+ if ( m_iProgressMsgIncrement < 5 )
+ {
+ m_iProgressMsgIncrement = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseAchievement::SetNextThink( float flThinkTime )
+{
+ m_pAchievementMgr->SetAchievementThink( this, flThinkTime );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseAchievement::ClearThink( void )
+{
+ m_pAchievementMgr->SetAchievementThink( this, THINK_CLEAR );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: see if we should award an achievement based on what just happened
+//-----------------------------------------------------------------------------
+void CBaseAchievement::EvaluateNewAchievement()
+{
+ if ( !IsAchieved() && m_iGoal > 0 && m_iCount >= m_iGoal )
+ {
+ AwardAchievement();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: determine if we should set this achievement to be achieved based
+// on other state. Used at init time.
+//-----------------------------------------------------------------------------
+void CBaseAchievement::EvaluateIsAlreadyAchieved()
+{
+ if ( !IsAchieved() && m_iGoal > 0 && m_iCount >= m_iGoal )
+ {
+ m_bAchieved = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called a map event for this achievement occurs
+//-----------------------------------------------------------------------------
+void CBaseAchievement::OnMapEvent( const char *pEventName )
+{
+ Assert( m_iFlags & ACH_LISTEN_MAP_EVENTS );
+
+ if ( 0 == Q_stricmp( pEventName, GetName() ) )
+ {
+ IncrementCount();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when an achievement is awarded
+//-----------------------------------------------------------------------------
+void CBaseAchievement::AwardAchievement()
+{
+ Assert( !IsAchieved() );
+ if ( IsAchieved() )
+ return;
+
+ m_pAchievementMgr->AwardAchievement( m_iAchievementID );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when a component of a multi-component event is found
+//-----------------------------------------------------------------------------
+void CBaseAchievement::OnComponentEvent( const char *pchComponentName )
+{
+ // find the component name in our list
+ for ( int i = 0; i < m_iNumComponents; i++ )
+ {
+ if ( 0 == Q_strcmp( pchComponentName, m_pszComponentNames[i] ) )
+ {
+ EnsureComponentBitSetAndEvaluate( i );
+ return;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the specified component bit # if it is not already.
+// If it does get set, evaluate if this satisfies an achievement
+//-----------------------------------------------------------------------------
+void CBaseAchievement::EnsureComponentBitSetAndEvaluate( int iBitNumber )
+{
+ Assert( iBitNumber < 64 ); // this is bit #, not a bit mask
+
+ if ( IsAchieved() )
+ return;
+
+ // calculate which bit this component corresponds to
+ uint64 iBitMask = ( (uint64) 1 ) << iBitNumber;
+
+ // see if we already have gotten this component
+ if ( 0 == ( iBitMask & m_iComponentBits ) )
+ {
+ if ( !AlwaysEnabled() && !m_pAchievementMgr->CheckAchievementsEnabled() )
+ {
+ Msg( "Achievements disabled, ignoring achievement component for %s\n", GetName() );
+ return;
+ }
+
+ // new component, set the bit and increment the count
+ SetComponentBits( m_iComponentBits | iBitMask );
+ Assert( m_iCount <= m_iGoal );
+ if ( m_iCount == m_iGoal )
+ {
+ // all components found, award the achievement (and save state)
+ AwardAchievement();
+ }
+ else
+ {
+ // save our state at the next good opportunity
+ m_pAchievementMgr->SetDirty( true );
+
+ if ( cc_achievement_debug.GetInt() )
+ {
+ Msg( "Component %d for achievement %s found\n", iBitNumber, GetName() );
+ }
+
+ ShowProgressNotification();
+ }
+ }
+ else
+ {
+ if ( cc_achievement_debug.GetInt() )
+ {
+ Msg( "Component %d for achievement %s found, but already had that component\n", iBitNumber, GetName() );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: displays achievement progress notification in the HUD
+//-----------------------------------------------------------------------------
+void CBaseAchievement::ShowProgressNotification()
+{
+ if ( !ShouldShowProgressNotification() )
+ return;
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "achievement_event" );
+ if ( event )
+ {
+ event->SetString( "achievement_name", GetName() );
+ event->SetInt( "cur_val", m_iCount );
+ event->SetInt( "max_val", m_iGoal );
+#ifdef GAME_DLL
+ gameeventmanager->FireEvent( event );
+#else
+ gameeventmanager->FireEventClientSide( event );
+#endif
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: clears dynamic state for this achievement
+//-----------------------------------------------------------------------------
+void CBaseAchievement::PreRestoreSavedGame()
+{
+ // if this achievement gets saved with the game, clear its state
+ if ( m_iFlags & ACH_SAVE_WITH_GAME )
+ {
+ m_iCount = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called after the data in this achievement has been restored from saved game
+//-----------------------------------------------------------------------------
+void CBaseAchievement::PostRestoreSavedGame()
+{
+ EvaluateIsAlreadyAchieved();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets component bits for this achievement
+//-----------------------------------------------------------------------------
+void CBaseAchievement::SetComponentBits( uint64 iComponentBits )
+{
+ Assert( m_iFlags & ACH_HAS_COMPONENTS );
+ // set the bit field
+ m_iComponentBits = iComponentBits;
+ // count how many bits are set and save that as the count
+ int iNumBitsSet = 0;
+ while ( iComponentBits > 0 )
+ {
+ if ( iComponentBits & 1 )
+ {
+ iNumBitsSet++;
+ }
+ iComponentBits >>= 1;
+ }
+ m_iCount = iNumBitsSet;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns whether we should save this achievement with a save game
+//-----------------------------------------------------------------------------
+bool CBaseAchievement::ShouldSaveWithGame()
+{
+ // save if we should get saved with the game, have a non-zero count, and have not
+ // been achieved (at which point the achievement state gets saved globally)
+ return ( ( m_iFlags & ACH_SAVE_WITH_GAME ) > 0 && ( GetCount() > 0 ) && !IsAchieved() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns whether we should save this achievement to the global file
+//-----------------------------------------------------------------------------
+bool CBaseAchievement::ShouldSaveGlobal()
+{
+ // 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
+ return ( ( ( m_iFlags & ACH_SAVE_GLOBAL ) > 0 && ( GetCount() > 0 ) ) || IsAchieved() || ( m_iProgressShown > 0 ) || ShouldShowOnHUD() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns whether this achievement is active
+//-----------------------------------------------------------------------------
+bool CBaseAchievement::IsActive()
+{
+ // we're not active if already achieved
+ if ( IsAchieved() )
+ return false;
+
+ // if there's a map filter and we're not on the specified map, we're not active
+ if ( ( m_pMapNameFilter ) && ( 0 != Q_strcmp( m_pAchievementMgr->GetMapName(), m_pMapNameFilter ) ) )
+ return false;
+
+ return true;
+}
+
+//=============================================================================
+// HPE_BEGIN:
+// [pfreese] Moved serialization from AchievementMgr to here so that derived
+// classes can persist additional data
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Purpose: Serialize our data to the KeyValues node
+//-----------------------------------------------------------------------------
+void CBaseAchievement::GetSettings( KeyValues *pNodeOut )
+{
+ pNodeOut->SetInt( "value", IsAchieved() ? 1 : 0 );
+
+ if ( HasComponents() )
+ {
+ pNodeOut->SetUint64( "data", m_iComponentBits );
+ }
+ else
+ {
+ if ( !IsAchieved() )
+ {
+ pNodeOut->SetInt( "data", m_iCount );
+ }
+ }
+ pNodeOut->SetInt( "hud", ShouldShowOnHUD() ? 1 : 0 );
+ pNodeOut->SetInt( "msg", m_iProgressShown );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Unserialize our data from the KeyValues node
+//-----------------------------------------------------------------------------
+void CBaseAchievement::ApplySettings( KeyValues *pNodeIn )
+{
+ // set the count
+ if ( pNodeIn->GetInt( "value" ) > 0 )
+ {
+ m_iCount = m_iGoal;
+ m_bAchieved = true;
+ }
+ else if ( !HasComponents() )
+ {
+ m_iCount = pNodeIn->GetInt( "data" );
+ }
+
+ // if this achievement has components, set the component bits
+ if ( HasComponents() )
+ {
+ int64 iComponentBits = pNodeIn->GetUint64( "data" );
+ SetComponentBits( iComponentBits );
+ }
+ SetShowOnHUD( !!pNodeIn->GetInt( "hud" ) );
+ m_iProgressShown = pNodeIn->GetInt( "msg" );
+}
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CFailableAchievement::CFailableAchievement() : CBaseAchievement()
+{
+ m_bFailed = false;
+ m_bActivated = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns whether we should save this achievement with a save game
+//-----------------------------------------------------------------------------
+bool CFailableAchievement::ShouldSaveWithGame()
+{
+ // save if we should get saved with the game, and are active or have failed
+ return ( ( ( m_iFlags & ACH_SAVE_WITH_GAME ) > 0 ) && ( m_bActivated || m_bFailed ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: clears dynamic state for this achievement
+//-----------------------------------------------------------------------------
+void CFailableAchievement::PreRestoreSavedGame()
+{
+ m_bFailed = false;
+ m_bActivated = false;
+
+ BaseClass::PreRestoreSavedGame();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: called after the data in this achievement has been restored from saved game
+//-----------------------------------------------------------------------------
+void CFailableAchievement::PostRestoreSavedGame()
+{
+ // if there is no activation event set for this achievement, it is always active, activate it now
+ if ( !m_bFailed && !GetActivationEventName()[0] )
+ {
+ m_bActivated = true;
+ }
+
+ if ( m_bActivated )
+ {
+ Activate();
+ }
+
+ BaseClass::PostRestoreSavedGame();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: called when a map event occurs
+//-----------------------------------------------------------------------------
+void CFailableAchievement::OnMapEvent( const char *pEventName )
+{
+ // if we're not activated and we got the activation event, activate
+ if ( !m_bActivated && ( 0 == Q_stricmp( pEventName, GetActivationEventName() ) ) )
+ {
+ OnActivationEvent();
+ }
+ // if this is the evaluation event, see if we've failed or not
+ else if ( m_bActivated && 0 == Q_stricmp( pEventName, GetEvaluationEventName() ) )
+ {
+ OnEvaluationEvent();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when this failable achievement is activated
+//-----------------------------------------------------------------------------
+void CFailableAchievement::Activate()
+{
+ m_bActivated = true;
+ ListenForEvents();
+ if ( cc_achievement_debug.GetInt() )
+ {
+ Msg( "Failable achievement %s now active\n", GetName() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when this failable achievement should be evaluated
+//-----------------------------------------------------------------------------
+void CFailableAchievement::OnEvaluationEvent()
+{
+ if ( !m_bFailed )
+ {
+ // we haven't failed and we reached the evaluation point, we've succeeded
+ IncrementCount();
+ }
+
+ if ( cc_achievement_debug.GetInt() )
+ {
+ Msg( "Failable achievement %s has been evaluated (%s), now inactive\n", GetName(), m_bFailed ? "FAILED" : "AWARDED" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets this achievement to failed
+//-----------------------------------------------------------------------------
+void CFailableAchievement::SetFailed()
+{
+ if ( !m_bFailed )
+ {
+ m_bFailed = true;
+
+ if ( cc_achievement_debug.GetInt() )
+ {
+ Msg( "Achievement failed: %s (%s)\n", GetName(), GetName() );
+ }
+ }
+}
+
+//===========================================
+
+void CAchievement_AchievedCount::Init()
+{
+ SetFlags( ACH_SAVE_GLOBAL );
+ SetGoal( 1 );
+ SetAchievementsRequired( 0, 0, 0 );
+}
+
+// Count how many achievements have been earned in our range
+void CAchievement_AchievedCount::OnSteamUserStatsStored( void )
+{
+ // DO NO CALL. REPLACED BY CHECKMETAACHIEVEMENTS!
+ Assert( 0 );
+}
+
+void CAchievement_AchievedCount::SetAchievementsRequired( int iNumRequired, int iLowRange, int iHighRange )
+{
+ m_iNumRequired = iNumRequired;
+ m_iLowRange = iLowRange;
+ m_iHighRange = iHighRange;
} \ No newline at end of file