You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
916 lines
25 KiB
916 lines
25 KiB
//========= 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 );
|
|
|