diff --git a/stepmania/src/GameState.cpp b/stepmania/src/GameState.cpp index 099e3a762f..4458f3ab69 100644 --- a/stepmania/src/GameState.cpp +++ b/stepmania/src/GameState.cpp @@ -245,6 +245,19 @@ void GameState::EndGame() pMachineProfile->m_iNumSongsPlayedByStyle[ss.style]++; pMachineProfile->m_iNumSongsPlayedByDifficulty[ss.pSteps[p]->GetDifficulty()]++; pMachineProfile->m_iNumSongsPlayedByMeter[iMeter]++; + pMachineProfile->m_iTotalDancePoints += ss.iActualDancePoints[p]; + if( ss.StageType == StageStats::STAGE_EXTRA || ss.StageType == StageStats::STAGE_EXTRA2 ) + { + if( ss.bFailed[p] ) + ++pMachineProfile->m_iNumExtraStagesFailed; + else + ++pMachineProfile->m_iNumExtraStagesPassed; + } + if( !ss.bFailed[p] ) + { + pMachineProfile->m_iNumSongsPassedByPlayMode[ss.playMode]++; + pMachineProfile->m_iNumSongsPassedByGrade[ss.GetGrade((PlayerNumber)p)]++; + } if( pPlayerProfile ) { @@ -252,6 +265,19 @@ void GameState::EndGame() pPlayerProfile->m_iNumSongsPlayedByStyle[ss.style]++; pPlayerProfile->m_iNumSongsPlayedByDifficulty[ss.pSteps[p]->GetDifficulty()]++; pPlayerProfile->m_iNumSongsPlayedByMeter[iMeter]++; + pPlayerProfile->m_iTotalDancePoints += ss.iActualDancePoints[p]; + if( ss.StageType == StageStats::STAGE_EXTRA || ss.StageType == StageStats::STAGE_EXTRA2 ) + { + if( ss.bFailed[p] ) + ++pPlayerProfile->m_iNumExtraStagesFailed; + else + ++pPlayerProfile->m_iNumExtraStagesPassed; + } + if( !ss.bFailed[p] ) + { + pPlayerProfile->m_iNumSongsPassedByPlayMode[ss.playMode]++; + pPlayerProfile->m_iNumSongsPassedByGrade[ss.GetGrade((PlayerNumber)p)]++; + } } } diff --git a/stepmania/src/Profile.cpp b/stepmania/src/Profile.cpp index ba62e9e6de..795d2e9ecd 100644 --- a/stepmania/src/Profile.cpp +++ b/stepmania/src/Profile.cpp @@ -72,6 +72,11 @@ void Profile::InitGeneralData() m_iTotalGameplaySeconds = 0; m_iCurrentCombo = 0; m_fCaloriesBurned = 0; + m_iTotalDancePoints = 0; + m_iNumExtraStagesPassed = 0; + m_iNumExtraStagesFailed = 0; + m_iNumToasties = 0; + m_UnlockedSongs.clear(); m_sLastMachinePlayed = ""; int i; @@ -83,6 +88,8 @@ void Profile::InitGeneralData() m_iNumSongsPlayedByDifficulty[i] = 0; for( i=0; iAppendChild( "CurrentCombo", m_iCurrentCombo ); pGeneralDataNode->AppendChild( "CaloriesBurned", m_fCaloriesBurned ); pGeneralDataNode->AppendChild( "LastMachinePlayed", m_sLastMachinePlayed ); + pGeneralDataNode->AppendChild( "TotalDancePoints", m_iTotalDancePoints ); + pGeneralDataNode->AppendChild( "NumExtraStagesPassed", m_iNumExtraStagesPassed ); + pGeneralDataNode->AppendChild( "NumExtraStagesFailed", m_iNumExtraStagesFailed ); + pGeneralDataNode->AppendChild( "NumToasties", m_iNumToasties ); + + { + XNode* pUnlockedSongs = pGeneralDataNode->AppendChild("UnlockedSongs"); + for( set::const_iterator it = m_UnlockedSongs.begin(); it != m_UnlockedSongs.end(); ++it ) + pUnlockedSongs->AppendChild( ssprintf("%i", *it) ); + } { XNode* pNumSongsPlayedByPlayMode = pGeneralDataNode->AppendChild("NumSongsPlayedByPlayMode"); @@ -528,6 +553,26 @@ XNode* Profile::SaveGeneralDataCreateNode() const } } + { + XNode* pNumSongsPassedByGrade = pGeneralDataNode->AppendChild("NumSongsPassedByGrade"); + FOREACH_Grade( g ) + { + if( !m_iNumSongsPassedByGrade[g] ) + continue; + pNumSongsPassedByGrade->AppendChild( GradeToString(g), m_iNumSongsPassedByGrade[g] ); + } + } + + { + XNode* pNumSongsPassedByPlayMode = pGeneralDataNode->AppendChild("NumSongsPassedByPlayMode"); + FOREACH_PlayMode( pm ) + { + /* Don't save unplayed PlayModes. */ + if( !m_iNumSongsPassedByPlayMode[pm] ) + continue; + pNumSongsPassedByPlayMode->AppendChild( PlayModeToString(pm), m_iNumSongsPassedByPlayMode[pm] ); + } + } return pGeneralDataNode; } @@ -575,6 +620,19 @@ void Profile::LoadGeneralDataFromNode( const XNode* pNode ) pNode->GetChildValue( "CurrentCombo", m_iCurrentCombo ); pNode->GetChildValue( "CaloriesBurned", m_fCaloriesBurned ); pNode->GetChildValue( "LastMachinePlayed", m_sLastMachinePlayed ); + pNode->GetChildValue( "TotalDancePoints", m_iTotalDancePoints ); + pNode->GetChildValue( "NumExtraStagesPassed", m_iNumExtraStagesPassed ); + pNode->GetChildValue( "NumExtraStagesFailed", m_iNumExtraStagesFailed ); + pNode->GetChildValue( "NumToasties", m_iNumToasties ); + + { + XNode* pUnlockedSongs = pNode->GetChild("UnlockedSongs"); + if( pUnlockedSongs ) + { + FOREACH_Node( pUnlockedSongs, song ) + m_UnlockedSongs.insert( atoi(song->name) ); + } + } { XNode* pNumSongsPlayedByPlayMode = pNode->GetChild("NumSongsPlayedByPlayMode"); @@ -623,6 +681,21 @@ void Profile::LoadGeneralDataFromNode( const XNode* pNode ) for( int i=0; iGetChildValue( ssprintf("Meter%d",i), m_iNumSongsPlayedByMeter[i] ); } + + { + XNode* pNumSongsPassedByGrade = pNode->GetChild("NumSongsPassedByGrade"); + if( pNumSongsPassedByGrade ) + FOREACH_Grade( g ) + pNumSongsPassedByGrade->GetChildValue( GradeToString(g), m_iNumSongsPassedByGrade[g] ); + } + + { + XNode* pNumSongsPassedByPlayMode = pNode->GetChild("NumSongsPassedByPlayMode"); + if( pNumSongsPassedByPlayMode ) + FOREACH_PlayMode( pm ) + pNumSongsPassedByPlayMode->GetChildValue( PlayModeToString(pm), m_iNumSongsPassedByPlayMode[pm] ); + + } } diff --git a/stepmania/src/Profile.h b/stepmania/src/Profile.h index e700befc4a..02f7e2df77 100644 --- a/stepmania/src/Profile.h +++ b/stepmania/src/Profile.h @@ -16,6 +16,7 @@ #include "Style.h" #include "Grade.h" #include +#include #include "XmlFile.h" #include "HighScore.h" @@ -66,7 +67,8 @@ public: // CString GetDisplayName() const; CString GetDisplayCaloriesBurned(); - int GetTotalNumSongsPlayed(); + int GetTotalNumSongsPlayed() const; + int GetTotalNumSongsPassed() const; static CString GetProfileDisplayNameFromDir( CString sDir ); int GetSongNumTimesPlayed( const Song* pSong ) const; @@ -87,11 +89,18 @@ public: int m_iTotalGameplaySeconds; int m_iCurrentCombo; int m_fCaloriesBurned; + int m_iTotalDancePoints; + int m_iNumExtraStagesPassed; + int m_iNumExtraStagesFailed; + int m_iNumToasties; + set m_UnlockedSongs; mutable CString m_sLastMachinePlayed; // mutable because we overwrite this on save, and I don't want to remove const from the whole save chain. -Chris int m_iNumSongsPlayedByPlayMode[NUM_PLAY_MODES]; int m_iNumSongsPlayedByStyle[NUM_STYLES]; int m_iNumSongsPlayedByDifficulty[NUM_DIFFICULTIES]; int m_iNumSongsPlayedByMeter[MAX_METER+1]; + int m_iNumSongsPassedByPlayMode[NUM_PLAY_MODES]; + int m_iNumSongsPassedByGrade[NUM_GRADES]; // diff --git a/stepmania/src/ProfileManager.cpp b/stepmania/src/ProfileManager.cpp index 326d87d8bf..ca64741f72 100644 --- a/stepmania/src/ProfileManager.cpp +++ b/stepmania/src/ProfileManager.cpp @@ -426,6 +426,15 @@ const Profile* ProfileManager::GetProfile( ProfileSlot slot ) const } } +// +// General +// +void ProfileManager::IncrementToastiesCount( PlayerNumber pn ) +{ + if( PROFILEMAN->IsUsingProfile(pn) ) + ++PROFILEMAN->GetProfile(pn)->m_iNumToasties; + ++PROFILEMAN->GetMachineProfile()->m_iNumToasties; +} // // Song stats diff --git a/stepmania/src/ProfileManager.h b/stepmania/src/ProfileManager.h index 3145bbac5b..d34203cf4a 100644 --- a/stepmania/src/ProfileManager.h +++ b/stepmania/src/ProfileManager.h @@ -37,6 +37,12 @@ public: bool LoadProfileFromMemoryCard( PlayerNumber pn ); bool SaveProfile( PlayerNumber pn ) const; void UnloadProfile( PlayerNumber pn ); + + // + // General data + // + void IncrementToastiesCount( PlayerNumber pn ); + // // High scores diff --git a/stepmania/src/ScoreKeeperMAX2.cpp b/stepmania/src/ScoreKeeperMAX2.cpp index 53ff2ef3e7..3f3bd1b3a9 100644 --- a/stepmania/src/ScoreKeeperMAX2.cpp +++ b/stepmania/src/ScoreKeeperMAX2.cpp @@ -18,12 +18,12 @@ #include "ScreenGameplay.h" #include "GameState.h" #include "Course.h" -#include "UnlockSystem.h" #include "SDL_utils.h" #include "SongManager.h" #include "NoteDataUtil.h" #include "RageLog.h" #include "StageStats.h" +#include "ProfileManager.h" ScoreKeeperMAX2::ScoreKeeperMAX2( const vector& apSongs, const vector& apNotes_, const vector &asModifiers, PlayerNumber pn_ ): @@ -391,7 +391,7 @@ void ScoreKeeperMAX2::HandleTapRowScore( TapNoteScore scoreOfLastTap, int iNumTa if( m_iCurToastyCombo==250 && !GAMESTATE->m_bDemonstrationOrJukebox ) { SCREENMAN->PostMessageToTopScreen( SM_PlayToasty, 0 ); - UNLOCKMAN->UnlockToasty(); + PROFILEMAN->IncrementToastiesCount( m_PlayerNumber ); } break; default: diff --git a/stepmania/src/ScreenEvaluation.cpp b/stepmania/src/ScreenEvaluation.cpp index 62e09dbc26..a52d6658e6 100644 --- a/stepmania/src/ScreenEvaluation.cpp +++ b/stepmania/src/ScreenEvaluation.cpp @@ -219,34 +219,6 @@ void ScreenEvaluation::Init() } - for( p=0; pIsPlayerEnabled( (PlayerNumber)p ) ) - continue; // skip - - switch( m_Type ) - { - case stage: - // update unlock data - UNLOCKMAN->UnlockClearStage(); - UNLOCKMAN->UnlockAddAP( grade[p] ); - UNLOCKMAN->UnlockAddSP( grade[p] ); - UNLOCKMAN->UnlockAddDP( (float)stageStats.iActualDancePoints[p] ); - - // if it's extra stage, update # passed stages - if ( (GAMESTATE->IsExtraStage() || GAMESTATE->IsExtraStage2() ) && - grade[p] > GRADE_FAILED ) - UNLOCKMAN->UnlockClearExtraStage(); - break; - - case course: - UNLOCKMAN->UnlockAddDP( (float) stageStats.iActualDancePoints[p] ); - UNLOCKMAN->UnlockAddAP( (float) stageStats.iSongsPassed[p] ); - UNLOCKMAN->UnlockAddSP( (float) stageStats.iSongsPassed[p] ); - break; - } - } - // // update persistent statistics // diff --git a/stepmania/src/ScreenGameplay.cpp b/stepmania/src/ScreenGameplay.cpp index 2dfc1490d4..7bb403467e 100644 --- a/stepmania/src/ScreenGameplay.cpp +++ b/stepmania/src/ScreenGameplay.cpp @@ -2135,9 +2135,6 @@ void ScreenGameplay::HandleScreenMessage( const ScreenMessage SM ) ASSERT( fMaxSurviveSeconds > 0 ); m_textSurviveTime.SetText( "TIME: " + SecondsToTime(fMaxSurviveSeconds) ); SET_XY_AND_ON_COMMAND( m_textSurviveTime ); - - // Increment update failed extra stage count. - UNLOCKMAN->UnlockFailExtraStage(); } // Feels hackish. Feel free to make cleaner. diff --git a/stepmania/src/UnlockSystem.cpp b/stepmania/src/UnlockSystem.cpp index a395cb95a4..eb1259ff5a 100644 --- a/stepmania/src/UnlockSystem.cpp +++ b/stepmania/src/UnlockSystem.cpp @@ -21,6 +21,7 @@ #include "GameState.h" #include "IniFile.h" #include "MsdFile.h" +#include "ProfileManager.h" UnlockSystem* UNLOCKMAN = NULL; // global and accessable from anywhere in our program @@ -43,12 +44,6 @@ UnlockSystem::UnlockSystem() UNLOCKMAN = this; Load(); - - memset( m_fScores, 0, sizeof(m_fScores) ); - - ReadValues(); // in case its ever accessed, - // we want the values to be available - WriteValues(); // create if it does not exist } void UnlockSystem::UnlockSong( const Song *song ) @@ -140,14 +135,97 @@ UnlockEntry::UnlockEntry() m_pCourse = NULL; } +static float GetArcadePoints( const Profile *pProfile ) +{ + float fAP = 0; + + FOREACH_Grade(g) + { + switch(g) + { + case GRADE_TIER_1: + case GRADE_TIER_2: fAP += 9 * pProfile->m_iNumSongsPassedByGrade[g]; break; + default: fAP += 1 * pProfile->m_iNumSongsPassedByGrade[g]; break; + + case GRADE_FAILED: + case GRADE_NO_DATA: + ; // no points + break; + } + } + + FOREACH_PlayMode(pm) + { + switch(pm) + { + case PLAY_MODE_NONSTOP: + case PLAY_MODE_ONI: + case PLAY_MODE_ENDLESS: + fAP += pProfile->m_iNumSongsPlayedByPlayMode[pm]; + break; + } + + } + + return fAP; +} + +static float GetSongPoints( const Profile *pProfile ) +{ + float fSP = 0; + + FOREACH_Grade(g) + { + switch( g ) + { + case GRADE_TIER_1:/*AAAA*/ fSP += 20 * pProfile->m_iNumSongsPassedByGrade[g]; break; + case GRADE_TIER_2:/*AAA*/ fSP += 10* pProfile->m_iNumSongsPassedByGrade[g]; break; + case GRADE_TIER_3:/*AA*/ fSP += 5* pProfile->m_iNumSongsPassedByGrade[g]; break; + case GRADE_TIER_4:/*A*/ fSP += 4* pProfile->m_iNumSongsPassedByGrade[g]; break; + case GRADE_TIER_5:/*B*/ fSP += 3* pProfile->m_iNumSongsPassedByGrade[g]; break; + case GRADE_TIER_6:/*C*/ fSP += 2* pProfile->m_iNumSongsPassedByGrade[g]; break; + case GRADE_TIER_7:/*D*/ fSP += 1* pProfile->m_iNumSongsPassedByGrade[g]; break; + case GRADE_FAILED: + case GRADE_NO_DATA: + ; // no points + break; + } + } + + FOREACH_PlayMode(pm) + { + switch(pm) + { + case PLAY_MODE_NONSTOP: + case PLAY_MODE_ONI: + case PLAY_MODE_ENDLESS: + fSP += pProfile->m_iNumSongsPlayedByPlayMode[pm]; + break; + } + + } + + return fSP; +} + +void UnlockSystem::GetPoints( const Profile *pProfile, float fScores[NUM_UNLOCK_TYPES] ) const +{ + fScores[UNLOCK_ARCADE_POINTS] = GetArcadePoints( pProfile ); + fScores[UNLOCK_SONG_POINTS] = GetSongPoints( pProfile ); + fScores[UNLOCK_DANCE_POINTS] = (float) pProfile->m_iTotalDancePoints; + fScores[UNLOCK_CLEARED] = (float) pProfile->GetTotalNumSongsPassed(); +} bool UnlockEntry::IsLocked() const { + float fScores[NUM_UNLOCK_TYPES]; + UNLOCKMAN->GetPoints( PROFILEMAN->GetMachineProfile(), fScores ); + for( int i = 0; i < NUM_UNLOCK_TYPES; ++i ) - if( m_fRequired[i] && UNLOCKMAN->m_fScores[i] >= m_fRequired[i] ) + if( m_fRequired[i] && fScores[i] >= m_fRequired[i] ) return false; - if( m_iCode != -1 && UNLOCKMAN->m_UnlockedCodes.find(m_iCode) != UNLOCKMAN->m_UnlockedCodes.end() ) + if( m_iCode != -1 && PROFILEMAN->GetMachineProfile()->m_UnlockedSongs.find(m_iCode) != PROFILEMAN->GetMachineProfile()->m_UnlockedSongs.end() ) return false; return true; @@ -239,7 +317,7 @@ bool UnlockSystem::Load() current.m_iCode = (int) val; if( unlock_type == "RO" ) { - m_UnlockedCodes.insert( (int)val ); + current.m_iCode = (int)val; m_RouletteCodes.insert( (int)val ); } } @@ -271,14 +349,17 @@ bool UnlockSystem::Load() float UnlockSystem::PointsUntilNextUnlock( UnlockType t ) const { + float fScores[NUM_UNLOCK_TYPES]; + UNLOCKMAN->GetPoints( PROFILEMAN->GetMachineProfile(), fScores ); + float fSmallestPoints = 400000000; // or an arbitrarily large value for( unsigned a=0; a m_fScores[t] ) + if( m_SongEntries[a].m_fRequired[t] > fScores[t] ) fSmallestPoints = min( fSmallestPoints, m_SongEntries[a].m_fRequired[t] ); if( fSmallestPoints == 400000000 ) return 0; // no match found - return fSmallestPoints - m_fScores[t]; + return fSmallestPoints - fScores[t]; } /* Update the song pointer. Only call this when it's likely to have changed, @@ -304,154 +385,15 @@ void UnlockSystem::UpdateSongs() } } -bool UnlockSystem::ReadValues() -{ - IniFile data; - data.SetPath( MEMCARD_PATH ); - - if( !data.ReadFile() ) - return false; - - for( int i = 0; i < NUM_UNLOCK_TYPES; ++i ) - data.GetValue( "Unlock", g_UnlockNames[i], m_fScores[i] ); - - m_UnlockedCodes.clear(); - CString s; - if( data.GetValue( "Unlock", "UnlockedCodes", s ) ) - { - vector sCodes; - split( s, ",", sCodes, true ); - for( unsigned c = 0; c < sCodes.size(); ++c ) - { - const int n = atoi( sCodes[c] ); - m_UnlockedCodes.insert( n ); - } - } - - return true; -} - - -bool UnlockSystem::WriteValues() const -{ - IniFile data; - - data.SetPath( MEMCARD_PATH ); - - for( int i = 0; i < NUM_UNLOCK_TYPES; ++i ) - data.SetValue( "Unlock", g_UnlockNames[i], m_fScores[i] ); - - vector sCodes; - for( set::const_iterator it = m_UnlockedCodes.begin(); it != m_UnlockedCodes.end(); ++it ) - sCodes.push_back( ssprintf("%i", *it) ); - - const CString list = join( ",", sCodes ); - data.SetValue( "Unlock", "UnlockedCodes", list ); - - data.WriteFile(); - - return true; -} - -void UnlockSystem::UnlockAddAP(float credit) -{ - ReadValues(); - m_fScores[UNLOCK_ARCADE_POINTS] += credit; - WriteValues(); -} - -void UnlockSystem::UnlockAddAP(Grade grade) -{ - ReadValues(); - switch( grade ) - { - case GRADE_FAILED: - ; // no points - break; - case GRADE_TIER_1: - case GRADE_TIER_2: - m_fScores[UNLOCK_ARCADE_POINTS] += 9; - break; - case GRADE_NO_DATA: - ASSERT(0); - break; - default: - m_fScores[UNLOCK_ARCADE_POINTS] += 1; - break; - } - WriteValues(); -} - -void UnlockSystem::UnlockAddDP(float credit) -{ - ReadValues(); - - // we don't want to ever take away dance points - if( credit > 0 ) - m_fScores[UNLOCK_DANCE_POINTS] += credit; - WriteValues(); -} - -void UnlockSystem::UnlockAddSP(float credit) -{ - ReadValues(); - m_fScores[UNLOCK_SONG_POINTS] += credit; - WriteValues(); -} - -void UnlockSystem::UnlockAddSP( Grade grade ) -{ - ReadValues(); - - // TODO: move these to PREFS - switch( grade ) - { - case GRADE_TIER_1:/*AAAA*/ m_fScores[UNLOCK_SONG_POINTS] += 20; break; - case GRADE_TIER_2:/*AAA*/ m_fScores[UNLOCK_SONG_POINTS] += 10; break; - case GRADE_TIER_3:/*AA*/ m_fScores[UNLOCK_SONG_POINTS] += 5; break; - case GRADE_TIER_4:/*A*/ m_fScores[UNLOCK_SONG_POINTS] += 4; break; - case GRADE_TIER_5:/*B*/ m_fScores[UNLOCK_SONG_POINTS] += 3; break; - case GRADE_TIER_6:/*C*/ m_fScores[UNLOCK_SONG_POINTS] += 2; break; - case GRADE_TIER_7:/*D*/ m_fScores[UNLOCK_SONG_POINTS] += 1; break; - } - - WriteValues(); -} - -void UnlockSystem::UnlockClearExtraStage() -{ - ReadValues(); - ++m_fScores[UNLOCK_EXTRA_CLEARED]; - WriteValues(); -} - -void UnlockSystem::UnlockFailExtraStage() -{ - ReadValues(); - ++m_fScores[UNLOCK_EXTRA_FAILED]; - WriteValues(); -} - -void UnlockSystem::UnlockClearStage() -{ - ReadValues(); - ++m_fScores[UNLOCK_CLEARED]; - WriteValues(); -} - -void UnlockSystem::UnlockToasty() -{ - ReadValues(); - ++m_fScores[UNLOCK_TOASTY]; - WriteValues(); -} void UnlockSystem::UnlockCode( int num ) { - ReadValues(); - m_UnlockedCodes.insert( num ); - WriteValues(); + FOREACH_PlayerNumber( pn ) + if( PROFILEMAN->IsUsingProfile(pn) ) + PROFILEMAN->GetProfile(pn)->m_UnlockedSongs.insert( num ); + + PROFILEMAN->GetMachineProfile()->m_UnlockedSongs.insert( num ); } int UnlockSystem::GetNumUnlocks() const diff --git a/stepmania/src/UnlockSystem.h b/stepmania/src/UnlockSystem.h index 9eda92d631..28f5da856c 100644 --- a/stepmania/src/UnlockSystem.h +++ b/stepmania/src/UnlockSystem.h @@ -17,6 +17,7 @@ */ class Song; +class Profile; enum UnlockType { @@ -73,17 +74,6 @@ public: void UpdateSongs(); - // functions that add to values - void UnlockAddAP(Grade credit); - void UnlockAddAP(float credit); - void UnlockAddDP(float credit); - void UnlockAddSP(Grade credit); - void UnlockAddSP(float credit); - void UnlockClearExtraStage(); - void UnlockFailExtraStage(); - void UnlockClearStage(); - void UnlockToasty(); - void UnlockCode( int num ); // unlocks the song's code @@ -94,18 +84,15 @@ public: // All locked songs are stored here vector m_SongEntries; + void GetPoints( const Profile *pProfile, float fScores[NUM_UNLOCK_TYPES] ) const; + private: - // read and write unlocks + // read unlocks bool Load(); - bool ReadValues(); - bool WriteValues() const; UnlockEntry *FindSong( const Song *pSong ); UnlockEntry *FindCourse( const Course *pCourse ); - // unlock values, cached - float m_fScores[NUM_UNLOCK_TYPES]; - set m_UnlockedCodes; set m_RouletteCodes; // "codes" which are available in roulette and which unlock if rouletted };