diff options
Diffstat (limited to 'gameui/BonusMapsDatabase.cpp')
| -rw-r--r-- | gameui/BonusMapsDatabase.cpp | 916 |
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 ); |