summaryrefslogtreecommitdiff
path: root/gameui/BonusMapsDatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gameui/BonusMapsDatabase.cpp')
-rw-r--r--gameui/BonusMapsDatabase.cpp916
1 files changed, 916 insertions, 0 deletions
diff --git a/gameui/BonusMapsDatabase.cpp b/gameui/BonusMapsDatabase.cpp
new file mode 100644
index 0000000..7e394ed
--- /dev/null
+++ b/gameui/BonusMapsDatabase.cpp
@@ -0,0 +1,916 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "BonusMapsDatabase.h"
+#include "EngineInterface.h"
+
+#include "tier1/convar.h"
+#include "tier1/utlbuffer.h"
+
+#include "filesystem.h"
+#include "ModInfo.h"
+#include "EngineInterface.h"
+#include "ixboxsystem.h"
+#include "KeyValues.h"
+#include "BasePanel.h"
+#include "GameUI_Interface.h"
+#include "BonusMapsDialog.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+#define MOD_DIR ( IsXbox() ? "DEFAULT_WRITE_PATH" : "MOD" )
+
+
+const char g_pszMedalNames[4][8] =
+{
+ "none",
+ "bronze",
+ "silver",
+ "gold"
+};
+
+
+const char *COM_GetModDirectory();
+
+
+bool WriteBonusMapSavedData( KeyValues *data )
+{
+ if ( IsX360() && ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED ) )
+ return false;
+
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ data->RecursiveSaveToFile( buf, 0 );
+
+ char szFilename[_MAX_PATH];
+
+ if ( IsX360() )
+ Q_snprintf( szFilename, sizeof( szFilename ), "cfg:/bonus_maps_data.bmd" );
+ else
+ Q_snprintf( szFilename, sizeof( szFilename ), "save/bonus_maps_data.bmd" );
+
+ bool bWriteSuccess = g_pFullFileSystem->WriteFile( szFilename, MOD_DIR, buf );
+
+ xboxsystem->FinishContainerWrites();
+
+ return bWriteSuccess;
+}
+
+void GetBooleanStatus( KeyValues *pBonusFilesKey, BonusMapDescription_t &map )
+{
+ KeyValues *pFileKey = NULL;
+ KeyValues *pBonusKey = NULL;
+
+ for ( pFileKey = pBonusFilesKey->GetFirstSubKey(); pFileKey; pFileKey = pFileKey->GetNextTrueSubKey() )
+ {
+ if ( Q_strcmp( pFileKey->GetName(), map.szFileName ) == 0 )
+ {
+ for ( pBonusKey = pFileKey->GetFirstSubKey(); pBonusKey; pBonusKey = pBonusKey->GetNextKey() )
+ {
+ if ( Q_strcmp( pBonusKey->GetName(), map.szMapName ) == 0 )
+ {
+ // Found the data
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if ( pBonusKey )
+ {
+ map.bLocked = ( pBonusKey->GetInt( "lock" ) != 0 );
+ map.bComplete = ( pBonusKey->GetInt( "complete" ) != 0 );
+ }
+}
+
+bool SetBooleanStatus( KeyValues *pBonusFilesKey, const char *pchName, const char *pchFileName, const char *pchMapName, bool bValue )
+{
+ // Don't create entries for files that don't exist
+ if ( !IsX360() && ! (g_pFullFileSystem->FileExists( pchFileName, "MOD" ) || g_pFullFileSystem->IsDirectory( pchFileName, "MOD" ) ) )
+ {
+ DevMsg( "Failed to set boolean status for file %s.", pchFileName );
+ return false;
+ }
+
+ bool bChanged = false;
+
+ KeyValues *pFileKey = NULL;
+ KeyValues *pBonusKey = NULL;
+
+ for ( pFileKey = pBonusFilesKey->GetFirstSubKey(); pFileKey; pFileKey = pFileKey->GetNextTrueSubKey() )
+ {
+ if ( Q_strcmp( pFileKey->GetName(), pchFileName ) == 0 )
+ {
+ for ( pBonusKey = pFileKey->GetFirstSubKey(); pBonusKey; pBonusKey = pBonusKey->GetNextKey() )
+ {
+ if ( Q_strcmp( pBonusKey->GetName(), pchMapName ) == 0 )
+ {
+ // Found the data
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if ( !pFileKey )
+ {
+ // Didn't find it, so create a new spot for the data
+ pFileKey = new KeyValues( pchFileName );
+ pBonusFilesKey->AddSubKey( pFileKey );
+ }
+
+ if ( !pBonusKey )
+ {
+ pBonusKey = new KeyValues( pchMapName, pchName, "0" );
+ pFileKey->AddSubKey( pBonusKey );
+ bChanged = true;
+ }
+
+ if ( ( pBonusKey->GetInt( pchName ) != 0 ) != bValue )
+ {
+ bChanged = true;
+ pBonusKey->SetInt( pchName, bValue );
+ }
+
+ return bChanged;
+}
+
+float GetChallengeBests( KeyValues *pBonusFilesKey, BonusMapDescription_t &challenge )
+{
+ // There's no challenges, so bail and assume 0% challenge completion
+ if ( challenge.m_pChallenges == NULL || challenge.m_pChallenges->Count() == 0 )
+ return 0.0f;
+
+ KeyValues *pFileKey = NULL;
+ KeyValues *pBonusKey = NULL;
+
+ for ( pFileKey = pBonusFilesKey->GetFirstSubKey(); pFileKey; pFileKey = pFileKey->GetNextTrueSubKey() )
+ {
+ if ( Q_strcmp( pFileKey->GetName(), challenge.szFileName ) == 0 )
+ {
+ for ( pBonusKey = pFileKey->GetFirstSubKey(); pBonusKey; pBonusKey = pBonusKey->GetNextKey() )
+ {
+ if ( Q_strcmp( pBonusKey->GetName(), challenge.szMapName ) == 0 )
+ {
+ // Found the data
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ float fChallengePoints = 0.0f;
+
+ for ( int iChallenge = 0; iChallenge < challenge.m_pChallenges->Count(); ++iChallenge )
+ {
+ ChallengeDescription_t *pChallengeDescription = &((*challenge.m_pChallenges)[ iChallenge ]);
+ pChallengeDescription->iBest = ( ( pBonusKey ) ? ( pBonusKey->GetInt( pChallengeDescription->szName, -1 ) ) : ( -1 ) );
+
+ if ( pChallengeDescription->iBest >= 0 )
+ {
+ if ( pChallengeDescription->iBest <= pChallengeDescription->iGold )
+ fChallengePoints += 3.0f;
+ else if ( pChallengeDescription->iBest <= pChallengeDescription->iSilver )
+ fChallengePoints += 2.0f;
+ else if ( pChallengeDescription->iBest <= pChallengeDescription->iBronze )
+ fChallengePoints += 1.0f;
+ }
+ }
+
+ return fChallengePoints / ( challenge.m_pChallenges->Count() * 3.0f );
+}
+
+bool UpdateChallengeBest( KeyValues *pBonusFilesKey, const BonusMapChallenge_t &challenge )
+{
+ // Don't create entries for files that don't exist
+ if ( !IsX360() && !g_pFullFileSystem->FileExists( challenge.szFileName, "MOD" ) )
+ {
+ DevMsg( "Failed to set challenge best for file %s.", challenge.szFileName );
+ return false;
+ }
+
+ bool bChanged = false;
+
+ KeyValues *pFileKey = NULL;
+ KeyValues *pBonusKey = NULL;
+
+ for ( pFileKey = pBonusFilesKey->GetFirstSubKey(); pFileKey; pFileKey = pFileKey->GetNextTrueSubKey() )
+ {
+ if ( Q_strcmp( pFileKey->GetName(), challenge.szFileName ) == 0 )
+ {
+ for ( pBonusKey = pFileKey->GetFirstSubKey(); pBonusKey; pBonusKey = pBonusKey->GetNextKey() )
+ {
+ if ( Q_strcmp( pBonusKey->GetName(), challenge.szMapName ) == 0 )
+ {
+ // Found the challenge
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if ( !pFileKey )
+ {
+ // Didn't find it, so create a new spot for data
+ pFileKey = new KeyValues( challenge.szFileName );
+ pBonusFilesKey->AddSubKey( pFileKey );
+ }
+
+ if ( !pBonusKey )
+ {
+ pBonusKey = new KeyValues( challenge.szMapName, challenge.szChallengeName, -1 );
+ pFileKey->AddSubKey( pBonusKey );
+ bChanged = true;
+ }
+
+ int iCurrentBest = pBonusKey->GetInt( challenge.szChallengeName, -1 );
+ if ( iCurrentBest == -1 || iCurrentBest > challenge.iBest )
+ {
+ bChanged = true;
+ pBonusKey->SetInt( challenge.szChallengeName, challenge.iBest );
+ }
+
+ return bChanged;
+}
+
+void GetChallengeMedals( ChallengeDescription_t *pChallengeDescription, int &iBest, int &iEarnedMedal, int &iNext, int &iNextMedal )
+{
+ iBest = pChallengeDescription->iBest;
+
+ if ( iBest == -1 )
+ iEarnedMedal = 0;
+ else if ( iBest <= pChallengeDescription->iGold )
+ iEarnedMedal = 3;
+ else if ( iBest <= pChallengeDescription->iSilver )
+ iEarnedMedal = 2;
+ else if ( iBest <= pChallengeDescription->iBronze )
+ iEarnedMedal = 1;
+ else
+ iEarnedMedal = 0;
+
+ iNext = -1;
+
+ switch ( iEarnedMedal )
+ {
+ case 0:
+ iNext = pChallengeDescription->iBronze;
+ iNextMedal = 1;
+ break;
+ case 1:
+ iNext = pChallengeDescription->iSilver;
+ iNextMedal = 2;
+ break;
+ case 2:
+ iNext = pChallengeDescription->iGold;
+ iNextMedal = 3;
+ break;
+ case 3:
+ iNext = -1;
+ iNextMedal = -1;
+ break;
+ }
+}
+
+
+CBonusMapsDatabase *g_pBonusMapsDatabase = NULL;
+
+CBonusMapsDatabase *BonusMapsDatabase( void )
+{
+ if ( !g_pBonusMapsDatabase )
+ static CBonusMapsDatabase StaticBonusMapsDatabase;
+
+ return g_pBonusMapsDatabase;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:Constructor
+//-----------------------------------------------------------------------------
+CBonusMapsDatabase::CBonusMapsDatabase( void )
+{
+ Assert( g_pBonusMapsDatabase == NULL ); // There should only be 1 bonus maps database
+ g_pBonusMapsDatabase = this;
+
+ RootPath();
+
+ m_pBonusMapsManifest = new KeyValues( "bonus_maps_manifest" );
+ m_pBonusMapsManifest->LoadFromFile( g_pFullFileSystem, "scripts/bonus_maps_manifest.txt", NULL );
+
+ m_iX360BonusesUnlocked = -1; // Only used on X360
+ m_bHasLoadedSaveData = false;
+
+ ReadBonusMapSaveData();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CBonusMapsDatabase::~CBonusMapsDatabase()
+{
+ g_pBonusMapsDatabase = NULL;
+}
+
+extern bool g_bIsCreatingNewGameMenuForPreFetching;
+
+bool CBonusMapsDatabase::ReadBonusMapSaveData( void )
+{
+ if ( !m_pBonusMapSavedData )
+ m_pBonusMapSavedData = new KeyValues( "bonus_map_saved_data" );
+
+ if ( g_bIsCreatingNewGameMenuForPreFetching )
+ {
+ // Although we may have a storage device it's not going to be able to find our file at this point! BAIL!
+ return false;
+ }
+
+#ifdef _X360
+ // Nothing to read
+ if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED )
+ return false;
+#endif
+
+ char szFilename[_MAX_PATH];
+
+ if ( IsX360() )
+ Q_snprintf( szFilename, sizeof( szFilename ), "cfg:/bonus_maps_data.bmd" );
+ else
+ Q_snprintf( szFilename, sizeof( szFilename ), "save/bonus_maps_data.bmd" );
+
+ m_pBonusMapSavedData->LoadFromFile( g_pFullFileSystem, szFilename, NULL );
+
+ m_bSavedDataChanged = false;
+ m_bHasLoadedSaveData = true;
+
+ return true;
+}
+
+bool CBonusMapsDatabase::WriteSaveData( void )
+{
+ bool bSuccess = false;
+
+ if ( m_bSavedDataChanged )
+ bSuccess = WriteBonusMapSavedData( m_pBonusMapSavedData );
+
+ if ( bSuccess )
+ m_bSavedDataChanged = false;
+
+ return bSuccess;
+}
+
+void CBonusMapsDatabase::RootPath( void )
+{
+ m_iDirDepth = 0;
+ V_strcpy_safe( m_szCurrentPath, "." );
+}
+
+void CBonusMapsDatabase::AppendPath( const char *pchAppend )
+{
+ ++m_iDirDepth;
+ char szCurPathTmp[MAX_PATH];
+ V_strcpy_safe( szCurPathTmp, m_szCurrentPath );
+ Q_snprintf( m_szCurrentPath, sizeof( m_szCurrentPath ), "%s/%s", szCurPathTmp, pchAppend );
+}
+
+void CBonusMapsDatabase::BackPath( void )
+{
+ if ( m_iDirDepth == 0 )
+ return;
+
+ if ( m_iDirDepth == 1 )
+ {
+ RootPath(); // back to root
+ return;
+ }
+
+ --m_iDirDepth;
+ Q_strrchr( m_szCurrentPath, '/' )[ 0 ] = '\0'; // remove a dir from the end
+}
+
+void CBonusMapsDatabase::SetPath( const char *pchPath, int iDirDepth )
+{
+ V_strcpy_safe( m_szCurrentPath, pchPath );
+ m_iDirDepth = iDirDepth;
+}
+
+void CBonusMapsDatabase::ClearBonusMapsList( void )
+{
+ m_BonusMaps.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: builds bonus map list from directory
+//-----------------------------------------------------------------------------
+void CBonusMapsDatabase::ScanBonusMaps( void )
+{
+ // Don't load in the bonus maps before we've properly read in the save data
+ if ( !m_bHasLoadedSaveData )
+ {
+ if ( ! ReadBonusMapSaveData() )
+ return;
+ }
+
+ char *pCurrentPath = &(m_szCurrentPath[2]);
+
+ // Reset completion percentage
+ m_iCompletableLevels = 0;
+ m_fCurrentCompletion = 0.0f;
+
+ // populate list box with all bonus maps in the current path
+ char szDirectory[_MAX_PATH];
+
+ if ( Q_strcmp( m_szCurrentPath, "." ) == 0 )
+ {
+ // We're at the root, so look at the directories in the manifest
+ KeyValues *pKey = NULL;
+ for ( pKey = m_pBonusMapsManifest->GetFirstSubKey(); pKey; pKey = pKey->GetNextKey() )
+ {
+ const char *pchType = pKey->GetName();
+ if ( Q_strcmp( pchType, "search" ) == 0 )
+ {
+ // Search through the directory
+ Q_snprintf( szDirectory, sizeof( szDirectory ), "%s/", pKey->GetString() );
+
+ BuildSubdirectoryList( szDirectory, true );
+ BuildBonusMapsList( szDirectory, true );
+ }
+ else if ( Q_strcmp( pchType, "dir" ) == 0 )
+ {
+ AddBonus( "", pKey->GetString(), true );
+ }
+ else if ( Q_strcmp( pchType, "map" ) == 0 )
+ {
+ AddBonus( "", pKey->GetString(), false );
+ }
+ }
+ }
+ else
+ {
+ // Search through the current directory
+ Q_snprintf( szDirectory, sizeof( szDirectory ), "%s/", pCurrentPath );
+
+ BuildSubdirectoryList( szDirectory, false );
+ BuildBonusMapsList( szDirectory, false );
+ }
+}
+
+void CBonusMapsDatabase::RefreshMapData( void )
+{
+ // Reset completion percentage
+ m_iCompletableLevels = 0;
+ m_fCurrentCompletion = 0.0f;
+
+ for ( int iMap = 0; iMap < m_BonusMaps.Count(); ++iMap )
+ {
+ BonusMapDescription_t *pMap = &m_BonusMaps[ iMap ];
+
+ float fCompletion = GetChallengeBests( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), *pMap );
+
+ // If all the challenges are completed set it as complete
+ if ( fCompletion == 1.0f )
+ SetBooleanStatus( "complete", pMap->szFileName, pMap->szMapName, true );
+
+ GetBooleanStatus( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), *pMap );
+
+ if ( pMap->bComplete )
+ fCompletion = 1.0f;
+
+ if ( !pMap->bIsFolder )
+ {
+ m_fCurrentCompletion += fCompletion;
+ ++m_iCompletableLevels;
+ }
+ }
+}
+
+int CBonusMapsDatabase::BonusCount( void )
+{
+ if ( m_BonusMaps.Count() == 0 )
+ ScanBonusMaps();
+
+ return m_BonusMaps.Count();
+}
+
+bool CBonusMapsDatabase::GetBlink( void )
+{
+ KeyValues *pBlinkKey = m_pBonusMapSavedData->FindKey( "blink" );
+ if ( !pBlinkKey )
+ return false;
+
+ return ( pBlinkKey->GetInt() != 0 );
+}
+
+void CBonusMapsDatabase::SetBlink( bool bState )
+{
+ KeyValues *pBlinkKey = m_pBonusMapSavedData->FindKey( "blink" );
+ if ( pBlinkKey )
+ {
+ bool bCurrentState = ( pBlinkKey->GetInt() != 0 );
+ if ( bState && !bCurrentState )
+ {
+ pBlinkKey->SetStringValue( "1" );
+ m_bSavedDataChanged = true;
+ }
+ else if ( !bState && bCurrentState )
+ {
+ pBlinkKey->SetStringValue( "0" );
+ m_bSavedDataChanged = true;
+ }
+ }
+}
+
+// Only used on X360
+bool CBonusMapsDatabase::BonusesUnlocked( void )
+{
+ if ( m_iX360BonusesUnlocked == -1 )
+ {
+ // Never checked, so set up the proper X360 scan
+ BonusMapsDatabase()->ClearBonusMapsList(); // clear the current list
+ BonusMapsDatabase()->RootPath();
+ BonusMapsDatabase()->ScanBonusMaps();
+
+ m_iX360BonusesUnlocked = 0;
+ }
+
+ if ( m_iX360BonusesUnlocked == 0 )
+ {
+ // Hasn't been recorded as unlocked yet
+ for ( int iBonusMap = 0; iBonusMap < BonusMapsDatabase()->BonusCount(); ++iBonusMap )
+ {
+ BonusMapDescription_t *pMap = BonusMapsDatabase()->GetBonusData( iBonusMap );
+ if ( Q_strcmp( pMap->szMapName, "#Bonus_Map_AdvancedChambers" ) == 0 && !pMap->bLocked )
+ {
+ // All bonuses unlocked, remember this and set up the proper X360 scan to get info.
+ m_iX360BonusesUnlocked = 1;
+ break;
+ }
+ }
+ }
+
+ return ( m_iX360BonusesUnlocked != 0 );
+}
+
+void CBonusMapsDatabase::SetCurrentChallengeNames( const char *pchFileName, const char *pchMapName, const char *pchChallengeName )
+{
+ V_strcpy_safe( m_CurrentChallengeNames.szFileName, pchFileName );
+ V_strcpy_safe( m_CurrentChallengeNames.szMapName, pchMapName );
+ V_strcpy_safe( m_CurrentChallengeNames.szChallengeName, pchChallengeName );
+}
+
+void CBonusMapsDatabase::GetCurrentChallengeNames( char *pchFileName, char *pchMapName, char *pchChallengeName )
+{
+ Q_strcpy( pchFileName, m_CurrentChallengeNames.szFileName );
+ Q_strcpy( pchMapName, m_CurrentChallengeNames.szMapName );
+ Q_strcpy( pchChallengeName, m_CurrentChallengeNames.szChallengeName );
+}
+
+void CBonusMapsDatabase::SetCurrentChallengeObjectives( int iBronze, int iSilver, int iGold )
+{
+ m_CurrentChallengeObjectives.iBronze = iBronze;
+ m_CurrentChallengeObjectives.iSilver = iSilver;
+ m_CurrentChallengeObjectives.iGold = iGold;
+}
+
+void CBonusMapsDatabase::GetCurrentChallengeObjectives( int &iBronze, int &iSilver, int &iGold )
+{
+ iBronze = m_CurrentChallengeObjectives.iBronze;
+ iSilver = m_CurrentChallengeObjectives.iSilver;
+ iGold = m_CurrentChallengeObjectives.iGold;
+}
+
+bool CBonusMapsDatabase::SetBooleanStatus( const char *pchName, const char *pchFileName, const char *pchMapName, bool bValue )
+{
+ bool bChanged = ::SetBooleanStatus( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), pchName, pchFileName, pchMapName, bValue );
+ if ( bChanged )
+ m_bSavedDataChanged = true;
+
+ return bChanged;
+}
+
+bool CBonusMapsDatabase::SetBooleanStatus( const char *pchName, int iIndex, bool bValue )
+{
+ BonusMapDescription_t *pMap = &(m_BonusMaps[iIndex]);
+
+ bool bChanged = SetBooleanStatus( pchName, pMap->szFileName, pMap->szMapName, bValue );
+ GetBooleanStatus( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), *pMap );
+
+ return bChanged;
+}
+
+bool CBonusMapsDatabase::UpdateChallengeBest( const char *pchFileName, const char *pchMapName, const char *pchChallengeName, int iBest )
+{
+ BonusMapChallenge_t challenge;
+ V_strcpy_safe( challenge.szFileName, pchFileName );
+ V_strcpy_safe( challenge.szMapName, pchMapName );
+ V_strcpy_safe( challenge.szChallengeName, pchChallengeName );
+ challenge.iBest = iBest;
+
+ bool bChanged = ::UpdateChallengeBest( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), challenge );
+ if ( bChanged )
+ m_bSavedDataChanged = true;
+
+ return bChanged;
+}
+
+float CBonusMapsDatabase::GetCompletionPercentage( void )
+{
+ // Avoid divide by zero
+ if ( m_iCompletableLevels <= 0 )
+ return 0.0f;
+
+ return m_fCurrentCompletion / m_iCompletableLevels;
+}
+
+int CBonusMapsDatabase::NumAdvancedComplete( void )
+{
+ char szCurrentPath[_MAX_PATH];
+ V_strcpy_safe( szCurrentPath, m_szCurrentPath );
+ int iDirDepth = m_iDirDepth;
+
+ BonusMapsDatabase()->ClearBonusMapsList();
+ SetPath( "./scripts/advanced_chambers", 1 );
+ ScanBonusMaps();
+
+ int iNumComplete = 0;
+
+ // Look through all the bonus maps
+ for ( int iBonusMap = 0; iBonusMap < BonusMapsDatabase()->BonusCount(); ++iBonusMap )
+ {
+ BonusMapDescription_t *pMap = BonusMapsDatabase()->GetBonusData( iBonusMap );
+
+ if ( pMap && Q_strstr( pMap->szMapName, "Advanced" ) != NULL )
+ {
+ // It's an advanced map, so check if it's complete
+ if ( pMap->bComplete )
+ ++iNumComplete;
+ }
+ }
+
+ BonusMapsDatabase()->ClearBonusMapsList();
+ SetPath( szCurrentPath, iDirDepth );
+ ScanBonusMaps();
+
+ return iNumComplete;
+}
+
+void CBonusMapsDatabase::NumMedals( int piNumMedals[ 3 ] )
+{
+ char szCurrentPath[_MAX_PATH];
+ V_strcpy_safe( szCurrentPath, m_szCurrentPath );
+ int iDirDepth = m_iDirDepth;
+
+ BonusMapsDatabase()->ClearBonusMapsList();
+ SetPath( "./scripts/challenges", 1 );
+ ScanBonusMaps();
+
+ for ( int i = 0; i < 3; ++i )
+ piNumMedals[ i ] = 0;
+
+ // Look through all the bonus maps
+ for ( int iBonusMap = 0; iBonusMap < BonusMapsDatabase()->BonusCount(); ++iBonusMap )
+ {
+ BonusMapDescription_t *pMap = BonusMapsDatabase()->GetBonusData( iBonusMap );
+
+ if ( pMap && pMap->m_pChallenges )
+ {
+ for ( int iChallenge = 0; iChallenge < pMap->m_pChallenges->Count(); ++iChallenge )
+ {
+ ChallengeDescription_t *pChallengeDescription = &((*pMap->m_pChallenges)[ iChallenge ]);
+
+ int iBest, iEarnedMedal, iNext, iNextMedal;
+ GetChallengeMedals( pChallengeDescription, iBest, iEarnedMedal, iNext, iNextMedal );
+
+ // Increase the count for this medal and every medal below it
+ while ( iEarnedMedal > 0 )
+ {
+ --iEarnedMedal; // Medals are 1,2&3 so subtract 1 first
+ piNumMedals[ iEarnedMedal ]++;
+ }
+ }
+ }
+ }
+
+ BonusMapsDatabase()->ClearBonusMapsList();
+ SetPath( szCurrentPath, iDirDepth );
+ ScanBonusMaps();
+}
+
+
+void CBonusMapsDatabase::AddBonus( const char *pCurrentPath, const char *pDirFileName, bool bIsFolder )
+{
+ char szFileName[_MAX_PATH];
+ Q_snprintf( szFileName, sizeof( szFileName ), "%s%s", pCurrentPath, pDirFileName );
+
+ // Only load bonus maps from the current mod's maps dir
+ if( !IsX360() && !( g_pFullFileSystem->IsDirectory( szFileName, "MOD" ) || g_pFullFileSystem->FileExists( szFileName, "MOD" ) ))
+ return;
+
+ ParseBonusMapData( szFileName, pDirFileName, bIsFolder );
+}
+
+void CBonusMapsDatabase::BuildSubdirectoryList( const char *pCurrentPath, bool bOutOfRoot )
+{
+ char szDirectory[_MAX_PATH];
+ Q_snprintf( szDirectory, sizeof( szDirectory ), "%s*", pCurrentPath );
+
+ FileFindHandle_t dirHandle;
+ const char *pDirFileName = g_pFullFileSystem->FindFirst( szDirectory, &dirHandle );
+
+ while (pDirFileName)
+ {
+ // Skip it if it's not a directory, is the root, is back, or is an invalid folder
+ if ( !g_pFullFileSystem->FindIsDirectory( dirHandle ) ||
+ Q_strcmp( pDirFileName, "." ) == 0 ||
+ Q_strcmp( pDirFileName, ".." ) == 0 ||
+ Q_stricmp( pDirFileName, "soundcache" ) == 0 ||
+ Q_stricmp( pDirFileName, "graphs" ) == 0 )
+ {
+ pDirFileName = g_pFullFileSystem->FindNext( dirHandle );
+ continue;
+ }
+
+ if ( !bOutOfRoot )
+ AddBonus( pCurrentPath, pDirFileName, true );
+ else
+ {
+ char szFileName[_MAX_PATH];
+ Q_snprintf( szFileName, sizeof( szFileName ), "%s%s", pCurrentPath, pDirFileName );
+ AddBonus( "", szFileName, true );
+ }
+
+ pDirFileName = g_pFullFileSystem->FindNext( dirHandle );
+ }
+
+ g_pFullFileSystem->FindClose( dirHandle );
+}
+
+void CBonusMapsDatabase::BuildBonusMapsList( const char *pCurrentPath, bool bOutOfRoot )
+{
+ char szDirectory[_MAX_PATH];
+ Q_snprintf( szDirectory, sizeof( szDirectory ), "%s*.bns", pCurrentPath );
+
+ FileFindHandle_t mapHandle;
+ const char *pMapFileName = g_pFullFileSystem->FindFirst( szDirectory, &mapHandle );
+
+ while ( pMapFileName && Q_strlen(pMapFileName)>0 )
+ {
+ // Skip it if it's a directory or is the folder info
+ if ( g_pFullFileSystem->FindIsDirectory( mapHandle ) || Q_strstr( pMapFileName, "folderinfo.bns" ) )
+ {
+ pMapFileName = g_pFullFileSystem->FindNext( mapHandle );
+ continue;
+ }
+
+ if ( !bOutOfRoot )
+ AddBonus( pCurrentPath, pMapFileName, false );
+ else
+ {
+ char szFileName[_MAX_PATH];
+ Q_snprintf( szFileName, sizeof( szFileName ), "%s%s", pCurrentPath, pMapFileName );
+ AddBonus( "", szFileName, false );
+ }
+
+ pMapFileName = g_pFullFileSystem->FindNext( mapHandle );
+ }
+
+ g_pFullFileSystem->FindClose( mapHandle );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parses the save game info out of the .sav file header
+//-----------------------------------------------------------------------------
+void CBonusMapsDatabase::ParseBonusMapData( char const *pszFileName, char const *pszShortName, bool bIsFolder )
+{
+ if ( !pszFileName || !pszShortName )
+ return;
+
+ char szMapInfo[_MAX_PATH];
+
+ // if it's a directory, there's no optional info
+ if ( bIsFolder )
+ {
+ // get the folder info file name
+ Q_snprintf( szMapInfo, sizeof(szMapInfo), "%s/folderinfo.bns", pszFileName );
+ }
+ else
+ {
+ // get the map info file name
+ Q_strncpy( szMapInfo, pszFileName, sizeof(szMapInfo) );
+ }
+
+ KeyValues *kv = new KeyValues( pszShortName );
+ if ( !kv->LoadFromFile( g_pFullFileSystem, szMapInfo, NULL ) )
+ DevMsg( "Unable to load bonus map info file %s\n", szMapInfo );
+
+ while ( kv )
+ {
+ int iMap = m_BonusMaps.AddToTail();
+
+ BonusMapDescription_t *pMap = &m_BonusMaps[ iMap ];
+
+ // set required map data
+ Q_strncpy( pMap->szFileName, pszFileName, sizeof(pMap->szFileName) );
+ Q_strncpy( pMap->szShortName, pszShortName, sizeof(pMap->szShortName) );
+ pMap->bIsFolder = bIsFolder;
+
+ // set optional map data
+ V_strcpy_safe( pMap->szMapName, kv->GetName() );
+ V_strcpy_safe( pMap->szMapFileName, kv->GetString( "map" ) );
+ V_strcpy_safe( pMap->szChapterName, kv->GetString( "chapter" ) );
+ V_strcpy_safe( pMap->szImageName, kv->GetString( "image" ) );
+ V_strcpy_safe( pMap->szComment, kv->GetString( "comment" ) );
+ pMap->bLocked = ( kv->GetInt( "lock", 0 ) != 0 );
+ pMap->bComplete = ( kv->GetInt( "complete", 0 ) != 0 );
+
+ float fCompletion = 0.0f;
+
+ KeyValues *pChallenges = kv->FindKey( "challenges" );
+
+ if ( pChallenges )
+ {
+ for ( KeyValues *pChallengeKey = pChallenges->GetFirstSubKey(); pChallengeKey; pChallengeKey = pChallengeKey->GetNextKey() )
+ {
+ if ( !pMap->m_pChallenges )
+ pMap->m_pChallenges = new CUtlVector<ChallengeDescription_t>;
+
+ int iChallenge = pMap->m_pChallenges->AddToTail();
+
+ ChallengeDescription_t *pChallenge = &(*pMap->m_pChallenges)[ iChallenge ];
+ V_strcpy_safe( pChallenge->szName, pChallengeKey->GetName() );
+ V_strcpy_safe( pChallenge->szComment, pChallengeKey->GetString( "comment" ) );
+ pChallenge->iType = pChallengeKey->GetInt( "type", -1 );
+ pChallenge->iBronze = pChallengeKey->GetInt( "bronze" );
+ pChallenge->iSilver = pChallengeKey->GetInt( "silver" );
+ pChallenge->iGold = pChallengeKey->GetInt( "gold" );
+ }
+
+ fCompletion = GetChallengeBests( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), *pMap );
+
+ // If all the challenges are completed set it as complete
+ if ( fCompletion == 1.0f )
+ SetBooleanStatus( "complete", pMap->szFileName, pMap->szMapName, true );
+ }
+
+ // Get boolean status last because it can be altered if all the challenges were completed
+ GetBooleanStatus( m_pBonusMapSavedData->FindKey( "bonusfiles", true ), *pMap );
+
+ if ( pMap->bComplete )
+ fCompletion = 1.0f;
+
+ if ( !pMap->bIsFolder )
+ {
+ m_fCurrentCompletion += fCompletion;
+ ++m_iCompletableLevels;
+ kv = kv->GetNextTrueSubKey();
+ }
+ else
+ kv = NULL;
+ }
+}
+
+
+void CC_BonusMapUnlock( const CCommand &args )
+{
+ if ( args.ArgC() < 3 )
+ {
+ GameUI().BonusMapUnlock();
+ return;
+ }
+
+ GameUI().BonusMapUnlock( args[ 1 ], args[ 2 ] );
+}
+static ConCommand sv_bonus_map_unlock("sv_bonus_map_unlock", CC_BonusMapUnlock, "Locks a bonus map.", FCVAR_CHEAT );
+
+
+void CC_BonusMapComplete( const CCommand &args )
+{
+ if ( args.ArgC() < 3 )
+ {
+ GameUI().BonusMapComplete();
+ return;
+ }
+
+ GameUI().BonusMapComplete( args[ 1 ], args[ 2 ] );
+}
+static ConCommand sv_bonus_map_complete("sv_bonus_map_complete", CC_BonusMapComplete, "Completes a bonus map.", FCVAR_CHEAT );
+
+
+void CC_BonusMapChallengeUpdate( const CCommand &args )
+{
+ if ( args.ArgC() < 5 )
+ {
+ return;
+ }
+
+ GameUI().BonusMapChallengeUpdate( args[ 1 ], args[ 2 ], args[ 3 ], atoi( args[ 4 ] ) );
+}
+static ConCommand sv_bonus_map_challenge_update("sv_bonus_map_challenge_update", CC_BonusMapChallengeUpdate, "Updates a bonus map challenge score.", FCVAR_CHEAT );