diff --git a/Docs/Changelog_sm-ssc.txt b/Docs/Changelog_sm-ssc.txt index 0275ae24e7..4f9c28b3c0 100644 --- a/Docs/Changelog_sm-ssc.txt +++ b/Docs/Changelog_sm-ssc.txt @@ -21,6 +21,20 @@ sm-ssc v1.2.3 | 201103?? * [MeterDisplay] Add Lua binding. "Allow setting and changing the width dynamically. Phase out the "StreamWidth" node property; set it with SetStreamWidth instead." [Glenn Maynard] +* sm5svn r28280: "Previously, we didn't create MusicWheel sorts when the screen + loads; it was delayed until the screen begins. This allowed preloading before + the style is set. However, it caused a major skip when the screen starts, + because creating the sorts is fairly expensive. + + Refactor this. Load the sorts when the screen is loaded, and then go through + them and quickly filter out unplayable data and handle other initialization + on begin. This is much faster, so we can more easily transition seamlessly + from the previous menu. + + One known limitation: the sorts are created before we know the style, so we + have to guess. This means if you're in a style like dance-solo or + pump-halfdouble, sorts will still be based on regular mode. This affects the + difficulty, top grade and roulette sorts." [Glenn Maynard] 20110305 -------- diff --git a/src/MusicWheel.cpp b/src/MusicWheel.cpp index d08d8b3df2..1428275d37 100644 --- a/src/MusicWheel.cpp +++ b/src/MusicWheel.cpp @@ -113,25 +113,31 @@ void MusicWheel::Load( RString sType ) * stable_sort) from its output, and title will be the secondary sort, without having * to re-sort by title each time. */ SONGMAN->SortSongs(); -} -void MusicWheel::BeginScreen() -{ RageTimer timer; RString times; /* Build all of the wheel item data. Do this after selecting the extra stage, * so it knows to always display it. */ FOREACH_ENUM( SortOrder, so ) { - BuildWheelItemDatas( m_WheelItemDatas[so], so ); + BuildWheelItemDatas( m_UnfilteredWheelItemDatas[so], so ); times += ssprintf( "%i:%.3f ", so, timer.GetDeltaTime() ); } LOG->Trace( "MusicWheel sorting took: %s", times.c_str() ); +} + +void MusicWheel::BeginScreen() +{ + FOREACH_ENUM( SortOrder, so ) + { + m_UnfilteredWheelItemDatas[so] = m_UnfilteredWheelItemDatas[so]; + UpdateWheelItemDatas( so ); + } /* Set m_LastModeMenuItem to the first item that matches the current mode. (Do this * after building wheel item data.) */ { - const vector &from = m_WheelItemDatas[SORT_MODE_MENU]; + const vector &from = m_UnfilteredWheelItemDatas[SORT_MODE_MENU]; for( unsigned i=0; im_pAction ); @@ -228,7 +234,7 @@ void MusicWheel::BeginScreen() MusicWheel::~MusicWheel() { FOREACH_ENUM( SortOrder, so ) - FOREACH( MusicWheelItemData*, m_WheelItemDatas[so], i ) + FOREACH( MusicWheelItemData*, m_UnfilteredWheelItemDatas[so], i ) delete *i; } @@ -247,7 +253,7 @@ bool MusicWheel::SelectSongOrCourse() return true; // Select the first selectable song based on the sort order... - vector &wiWheelItems = m_WheelItemDatas[GAMESTATE->m_SortOrder]; + vector &wiWheelItems = m_UnfilteredWheelItemDatas[GAMESTATE->m_SortOrder]; for( unsigned i = 0; i < wiWheelItems.size(); i++ ) { if( wiWheelItems[i]->m_pSong ) @@ -280,7 +286,7 @@ bool MusicWheel::SelectSong( const Song *p ) return false; unsigned i; - vector &from = m_WheelItemDatas[GAMESTATE->m_SortOrder]; + vector &from = m_UnfilteredWheelItemDatas[GAMESTATE->m_SortOrder]; for( i=0; im_pSong == p ) @@ -308,7 +314,7 @@ bool MusicWheel::SelectCourse( const Course *p ) return false; unsigned i; - vector &from = m_WheelItemDatas[GAMESTATE->m_SortOrder]; + vector &from = m_UnfilteredWheelItemDatas[GAMESTATE->m_SortOrder]; for( i=0; im_pCourse == p ) @@ -335,7 +341,7 @@ bool MusicWheel::SelectModeMenuItem() { /* Select the last-chosen option. */ ASSERT( GAMESTATE->m_SortOrder == SORT_MODE_MENU ); - const vector &from = m_WheelItemDatas[GAMESTATE->m_SortOrder]; + const vector &from = m_UnfilteredWheelItemDatas[GAMESTATE->m_SortOrder]; unsigned i; for( i=0; i &arraySongs, SortOrder so ) -{ - vector apAllSongs; - switch( so ) - { - case SORT_PREFERRED: - SONGMAN->GetPreferredSortSongs( apAllSongs ); - break; - case SORT_POPULARITY: - SONGMAN->GetPopularSongs(); - break; - case SORT_GROUP: - // if we're not using sections with a preferred song group, and there - // is a group to load, only load those songs. -aj - if(GAMESTATE->m_sPreferredSongGroup != GROUP_ALL && !USE_SECTIONS_WITH_PREFERRED_GROUP ) - { - apAllSongs = SONGMAN->GetSongs(GAMESTATE->m_sPreferredSongGroup); - break; - } - // otherwise fall through - default: - apAllSongs = SONGMAN->GetAllSongs(); - break; - } - - // filter songs that we don't have enough stages to play - { - vector vTempSongs; - SongCriteria sc; - sc.m_iMaxStagesForSong = GAMESTATE->GetSmallestNumStagesLeftForAnyHumanPlayer(); - SongUtil::FilterSongs( sc, apAllSongs, vTempSongs ); - apAllSongs = vTempSongs; - } - - // copy only songs that have at least one Steps for the current GameMode - for( unsigned i=0; iSongIsLocked( pSong ); - if( iLocked & LOCKED_DISABLED ) - continue; - - // If we're on an extra stage, and this song is selected, ignore #SELECTABLE. - if( pSong != GAMESTATE->m_pCurSong || !GAMESTATE->IsAnExtraStage() ) - { - // Hide songs that asked to be hidden via #SELECTABLE. - if( iLocked & LOCKED_SELECTABLE ) - continue; - if( so != SORT_ROULETTE && iLocked & LOCKED_ROULETTE ) - continue; - } - - /* Hide locked songs. If RANDOM_PICKS_LOCKED_SONGS, hide in Roulette - * and Random, too. */ - if( (so!=SORT_ROULETTE || !RANDOM_PICKS_LOCKED_SONGS) && iLocked ) - continue; - - if( PREFSMAN->m_bOnlyPreferredDifficulties ) - { - // if the song has steps that fit the preferred difficulty of the default player - if( pSong->HasStepsTypeAndDifficulty( GAMESTATE->GetCurrentStyle()->m_StepsType,GAMESTATE->m_PreferredDifficulty[GAMESTATE->GetFirstHumanPlayer()] ) ) - arraySongs.push_back( pSong ); - } - else - { - if(CommonMetrics::AUTO_SET_STYLE) - { - // with AUTO_SET_STYLE on and Autogen off, some songs may get - // hidden. Search through every playable StepsType until you - // find one, then add the song. - // see Issue 147 for more information. -aj - // http://ssc.ajworld.net/sm-ssc/bugtracker/view.php?id=147 - set vStepsType; - SongUtil::GetPlayableStepsTypes( pSong, vStepsType ); - - FOREACHS( StepsType, vStepsType, st ) - { - if(pSong->HasStepsType(*st)) - { - arraySongs.push_back( pSong ); - break; - } - } - } - else - { - // If the song has at least one steps, add it. - if( pSong->HasStepsType(GAMESTATE->GetCurrentStyle()->m_StepsType) ) - arraySongs.push_back( pSong ); - } - } - } - - /* Hack: Add extra stage item if it was eliminated for any reason - * (eg. it's a long song). */ - if( GAMESTATE->IsAnExtraStage() ) - { - Song* pSong; - Steps* pSteps; - SONGMAN->GetExtraStageInfo( GAMESTATE->IsExtraStage2(), GAMESTATE->GetCurrentStyle(), pSong, pSteps ); - - if( find( arraySongs.begin(), arraySongs.end(), pSong ) == arraySongs.end() ) - arraySongs.push_back( pSong ); - } -} - void MusicWheel::BuildWheelItemDatas( vector &arrayWheelItemDatas, SortOrder so ) { switch( so ) { case SORT_MODE_MENU: { - arrayWheelItemDatas.clear(); // clear out the previous wheel items vector vsNames; split( MODE_MENU_CHOICE_NAMES, ",", vsNames ); for( unsigned i=0; i &arrayWheelIt case SORT_LENGTH: case SORT_RECENT: { - // Make an array of Song*, then sort them + // Get all songs that can be shown in this sort. Filtering songs + // that can't be played will happen later, in UpdateWheelItemDatas. vector arraySongs; - - GetSongList( arraySongs, so ); + switch( so ) + { + case SORT_PREFERRED: + SONGMAN->GetPreferredSortSongs( arraySongs ); + break; + case SORT_POPULARITY: + arraySongs = SONGMAN->GetPopularSongs(); + break; + default: + arraySongs = SONGMAN->GetAllSongs(); + break; + } bool bUseSections = true; @@ -522,11 +431,16 @@ void MusicWheel::BuildWheelItemDatas( vector &arrayWheelIt // obey order specified by the preferred sort list break; case SORT_ROULETTE: - SongUtil::SortSongPointerArrayByStepsTypeAndMeter( arraySongs, GAMESTATE->m_pCurStyle->m_StepsType, Difficulty_Easy ); + { + StepsType st; + Difficulty dc; + SongUtil::GetStepsTypeAndDifficultyFromSortOrder( SORT_EASY_METER, st, dc ); + SongUtil::SortSongPointerArrayByStepsTypeAndMeter( arraySongs, st, dc ); if( (bool)PREFSMAN->m_bPreferredSortUsesGroups ) stable_sort( arraySongs.begin(), arraySongs.end(), SongUtil::CompareSongPointersByGroup ); bUseSections = false; break; + } case SORT_GROUP: SongUtil::SortSongPointerArrayByGroupAndTitle( arraySongs ); if(USE_SECTIONS_WITH_PREFERRED_GROUP) @@ -631,15 +545,6 @@ void MusicWheel::BuildWheelItemDatas( vector &arrayWheelIt { int iSectionCount = 0; - // Count songs in this section - unsigned j; - for( j=i; j < arraySongs.size(); j++ ) - { - if( SongUtil::GetSectionNameFromSongAndSort( arraySongs[j], so ) != sThisSection ) - break; - } - iSectionCount = j-i; - // new section, make a section item // todo: preferred sort section color handling? -aj RageColor colorSection = (so==SORT_GROUP) ? SONGMAN->GetSongGroupColor(pSong->m_sGroupName) : SECTION_COLORS.GetValue(iSectionColorIndex); @@ -647,7 +552,7 @@ void MusicWheel::BuildWheelItemDatas( vector &arrayWheelIt // In certain situations (e.g. simulating Pump it Up), themes may // want to only show one group at a time. if( !HIDE_INACTIVE_SECTIONS ) - arrayWheelItemDatas.push_back( new MusicWheelItemData(TYPE_SECTION, NULL, sThisSection, NULL, colorSection, iSectionCount) ); + arrayWheelItemDatas.push_back( new MusicWheelItemData(TYPE_SECTION, NULL, sThisSection, NULL, colorSection, 0) ); sLastSection = sThisSection; } } @@ -659,16 +564,10 @@ void MusicWheel::BuildWheelItemDatas( vector &arrayWheelIt if( SHOW_ROULETTE ) arrayWheelItemDatas.push_back( new MusicWheelItemData(TYPE_ROULETTE, NULL, "", NULL, ROULETTE_COLOR, 0) ); - // Only add TYPE_PORTAL if there's at least one song on the list. - bool bFoundAnySong = false; - for( unsigned i=0; !bFoundAnySong && i < arrayWheelItemDatas.size(); i++ ) - if( arrayWheelItemDatas[i]->m_Type == TYPE_SONG ) - bFoundAnySong = true; - - if( SHOW_RANDOM && bFoundAnySong ) + if( SHOW_RANDOM ) arrayWheelItemDatas.push_back( new MusicWheelItemData(TYPE_RANDOM, NULL, "", NULL, RANDOM_COLOR, 0) ); - if( SHOW_PORTAL && bFoundAnySong ) + if( SHOW_PORTAL ) arrayWheelItemDatas.push_back( new MusicWheelItemData(TYPE_PORTAL, NULL, "", NULL, PORTAL_COLOR, 0) ); // add custom wheel items @@ -769,8 +668,6 @@ void MusicWheel::BuildWheelItemDatas( vector &arrayWheelIt if( so == SORT_ALL_COURSES ) CourseUtil::SortCoursePointerArrayByType( apCourses ); - arrayWheelItemDatas.clear(); // clear out the previous wheel items - RString sLastSection = ""; int iSectionColorIndex = 0; for( unsigned i=0; i &arrayWheelIt } } - // check that this course has at least one song playable in the current style - if( !pCourse->IsPlayableIn(GAMESTATE->GetCurrentStyle()->m_StepsType) ) - continue; - if( sThisSection != sLastSection ) // new section, make a section item { RageColor c = SECTION_COLORS.GetValue(iSectionColorIndex); @@ -817,7 +710,6 @@ void MusicWheel::BuildWheelItemDatas( vector &arrayWheelIt MusicWheelItemData& WID = *arrayWheelItemDatas[i]; if( WID.m_pSong != NULL ) { - WID.m_Flags.bHasBeginnerOr1Meter = WID.m_pSong->IsEasy( GAMESTATE->GetCurrentStyle()->m_StepsType ) && SHOW_EASY_FLAG; WID.m_Flags.bEdits = false; set vStepsType; SongUtil::GetPlayableStepsTypes( WID.m_pSong, vStepsType ); @@ -827,25 +719,171 @@ void MusicWheel::BuildWheelItemDatas( vector &arrayWheelIt } else if( WID.m_pCourse != NULL ) { - WID.m_Flags.bHasBeginnerOr1Meter = false; WID.m_Flags.bEdits = WID.m_pCourse->IsAnEdit(); WID.m_Flags.iStagesForSong = 1; } } +} - // init crowns +/* The screen is starting. Take m_UnfilteredWheelItemDatas created on load by + * BuildWheelItemDatas and update them for the current state. Output the + * results into m_WheelItemDatas. */ +void MusicWheel::UpdateWheelItemDatas( SortOrder so ) +{ + vector &aWheelItemDatas = m_UnfilteredWheelItemDatas[so]; + + // Only add TYPE_PORTAL if there's at least one song on the list. + bool bFoundAnySong = false; + for( unsigned i=0; !bFoundAnySong && i < aWheelItemDatas.size(); i++ ) + if( aWheelItemDatas[i]->m_Type == TYPE_SONG ) + bFoundAnySong = true; + + vector aiRemove; + aiRemove.insert( aiRemove.begin(), aWheelItemDatas.size(), false ); + + SongCriteria sc; + sc.m_iMaxStagesForSong = GAMESTATE->GetSmallestNumStagesLeftForAnyHumanPlayer(); + + Song *pExtraStageSong = NULL; + if( GAMESTATE->IsAnExtraStage() ) + { + Steps *pSteps; + SONGMAN->GetExtraStageInfo( GAMESTATE->IsExtraStage2(), GAMESTATE->GetCurrentStyle(), pExtraStageSong, pSteps ); + } + + // Mark any songs that aren't playable in aiRemove. + for( unsigned i=0; i< aWheelItemDatas.size(); i++ ) + { + MusicWheelItemData& WID = *aWheelItemDatas[i]; + + // If we have no songs, remove Random and Portal. + if( WID.m_Type == TYPE_RANDOM || WID.m_Type == TYPE_PORTAL ) + { + if( !bFoundAnySong ) + aiRemove[i] = true; + continue; + } + + // Filter songs that we don't have enough stages to play. + if( WID.m_Type == TYPE_SONG ) + { + Song* pSong = WID.m_pSong; + + // Never remove the extra stage song. + if( pExtraStageSong && WID.m_pSong == pExtraStageSong ) + continue; + + // Check that we have enough stages to play this song, and that it's not disabled. + if( !sc.Matches(WID.m_pSong) ) + { + aiRemove[i] = true; + continue; + } + + int iLocked = UNLOCKMAN->SongIsLocked( pSong ); + + // If we're on an extra stage, and this song is selected, ignore #SELECTABLE. + if( pSong != GAMESTATE->m_pCurSong || !GAMESTATE->IsAnExtraStage() ) + { + // Hide songs that asked to be hidden via #SELECTABLE. + if( iLocked & LOCKED_SELECTABLE ) + { + aiRemove[i] = true; + continue; + } + if( so != SORT_ROULETTE && iLocked & LOCKED_ROULETTE ) + { + aiRemove[i] = true; + continue; + } + } + + /* Hide locked songs. If RANDOM_PICKS_LOCKED_SONGS, hide in Roulette + * and Random, too. */ + if( (so!=SORT_ROULETTE || !RANDOM_PICKS_LOCKED_SONGS) && iLocked ) + { + aiRemove[i] = true; + continue; + } + + // If the song has no steps for the current style, remove it. + if( !pSong->HasStepsType(GAMESTATE->GetCurrentStyle()->m_StepsType) ) + { + aiRemove[i] = true; + continue; + } + } + + if( WID.m_Type == TYPE_COURSE ) + { + if( !WID.m_pCourse->IsPlayableIn(GAMESTATE->GetCurrentStyle()->m_StepsType) ) + aiRemove[i] = true; + } + } + + // Filter out the songs we're removing. + vector &aFilteredData = m_WheelItemDatas[so]; + aFilteredData.reserve( aWheelItemDatas.size() ); + for( unsigned i=0; i< aWheelItemDatas.size(); i++ ) + { + if( aiRemove[i] ) + continue; + aFilteredData.push_back( aWheelItemDatas[i] ); + } + + // Update the song count in each section header. + for( unsigned i=0; i < aFilteredData.size(); ) + { + MusicWheelItemData& WID = *aFilteredData[i]; + ++i; + if( WID.m_Type != TYPE_SECTION ) + continue; + + // Count songs in this section + WID.m_iSectionCount = 0; + for( ; i < aFilteredData.size() && aFilteredData[i]->m_sText == WID.m_sText; ++i ) + ++WID.m_iSectionCount; + } + + // If we have any section headers with no songs, then we filtered all of + // the songs in that group, so remove it. This isn't optimized like the + // above since this is a rare case. + for( unsigned i=0; i < aFilteredData.size(); ++i ) + { + MusicWheelItemData& WID = *aFilteredData[i]; + if( WID.m_Type != TYPE_SECTION ) + continue; + if( WID.m_iSectionCount > 0 ) + continue; + aFilteredData.erase( aFilteredData.begin()+i, aFilteredData.begin()+i+1 ); + --i; + } + + // Update the popularity. This is affected by filtering. if( so == SORT_POPULARITY ) { - // init crown icons - for( unsigned i=0; i< min(3u,arrayWheelItemDatas.size()); i++ ) + for( unsigned i=0; i< min(3u,aFilteredData.size()); i++ ) { - MusicWheelItemData& WID = *arrayWheelItemDatas[i]; + MusicWheelItemData& WID = *aFilteredData[i]; WID.m_Flags.iPlayersBestNumber = i+1; } } - if( arrayWheelItemDatas.empty() ) - arrayWheelItemDatas.push_back( new MusicWheelItemData(TYPE_SECTION, NULL, "- EMPTY -", NULL, RageColor(1,0,0,1), 0) ); + // Update the easy status. This is affected by the steps type. + if( SHOW_EASY_FLAG ) + { + for( unsigned i=0; iIsEasy( GAMESTATE->GetCurrentStyle()->m_StepsType ); + } + } + + // If we've filtered all items, insert a dummy. + if( aFilteredData.empty() ) + aFilteredData.push_back( new MusicWheelItemData(TYPE_SECTION, NULL, "- EMPTY -", NULL, RageColor(1,0,0,1), 0) ); } void MusicWheel::UpdateSwitch() @@ -973,7 +1011,7 @@ bool MusicWheel::ChangeSort( SortOrder new_so ) // return true if change success return false; // Don't change to SORT_MODE_MENU if it doesn't have at least two choices. - if( new_so == SORT_MODE_MENU && m_WheelItemDatas[new_so].size() < 2 ) + if( new_so == SORT_MODE_MENU && m_UnfilteredWheelItemDatas[new_so].size() < 2 ) return false; switch( m_WheelState ) @@ -1109,7 +1147,7 @@ void MusicWheel::StartRandom() { // Shuffle and use the roulette wheel. RandomGen rnd; - random_shuffle( m_WheelItemDatas[SORT_ROULETTE].begin(), m_WheelItemDatas[SORT_ROULETTE].end(), rnd ); + random_shuffle( m_UnfilteredWheelItemDatas[SORT_ROULETTE].begin(), m_UnfilteredWheelItemDatas[SORT_ROULETTE].end(), rnd ); GAMESTATE->m_SortOrder.Set( SORT_ROULETTE ); } else @@ -1147,7 +1185,7 @@ void MusicWheel::SetOpenSection( RString group ) GAMEMAN->GetCompatibleStyles( GAMESTATE->m_pCurGame, GAMESTATE->GetNumPlayersEnabled(), vpPossibleStyles ); m_CurWheelItemData.clear(); - vector &from = m_WheelItemDatas[GAMESTATE->m_SortOrder]; + vector &from = m_UnfilteredWheelItemDatas[GAMESTATE->m_SortOrder]; m_CurWheelItemData.reserve( from.size() ); for( unsigned i = 0; i < from.size(); ++i ) { @@ -1309,7 +1347,7 @@ void MusicWheel::PlayerJoined() // We need to rebuild the wheel item data in this situation. -aj if( !GAMESTATE->IsCourseMode() && !PREFSMAN->m_bAutogenSteps ) { - BuildWheelItemDatas( m_WheelItemDatas[GAMESTATE->m_SortOrder], GAMESTATE->m_SortOrder ); + BuildWheelItemDatas( m_UnfilteredWheelItemDatas[GAMESTATE->m_SortOrder], GAMESTATE->m_SortOrder ); } SetOpenSection( m_sExpandedSectionName ); @@ -1359,7 +1397,7 @@ Song *MusicWheel::GetPreferredSelectionForRandomOrPortal() } RString sPreferredGroup = m_sExpandedSectionName; - vector &wid = m_WheelItemDatas[GAMESTATE->m_SortOrder]; + vector &wid = m_UnfilteredWheelItemDatas[GAMESTATE->m_SortOrder]; StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType; diff --git a/src/MusicWheel.h b/src/MusicWheel.h index d282473b86..18088ff3d7 100644 --- a/src/MusicWheel.h +++ b/src/MusicWheel.h @@ -52,15 +52,16 @@ public: protected: MusicWheelItem *MakeItem(); - void GetSongList( vector &arraySongs, SortOrder so ); void BuildWheelItemDatas( vector &arrayWheelItems, SortOrder so ); + void UpdateWheelItemDatas( SortOrder so ); bool SelectSongOrCourse(); bool SelectCourse( const Course *p ); bool SelectModeMenuItem(); virtual void UpdateSwitch(); - vector m_WheelItemDatas[NUM_SortOrder]; + vector m_UnfilteredWheelItemDatas[NUM_SortOrder]; + vector m_WheelItemDatas[NUM_SortOrder]; // aliases into m_UnfilteredWheelItemDatas RString m_sLastModeMenuItem; SortOrder m_SortOrder; diff --git a/src/SongUtil.cpp b/src/SongUtil.cpp index 6e075c3334..6ea6b413c8 100644 --- a/src/SongUtil.cpp +++ b/src/SongUtil.cpp @@ -460,8 +460,12 @@ static bool CompAscending( const pair &a, const pair &vpSongsInOut, bool bDescending ) { - /* Optimize by pre-writing a string to compare, since doing GetNumNotesWithGrade - * inside the sort is too slow. */ + StepsType st; + Difficulty dc; + SongUtil::GetStepsTypeAndDifficultyFromSortOrder( SORT_EASY_METER, st, dc ); + + /* Optimize by pre-writing a string to compare, since doing + * GetNumNotesWithGrade inside the sort is too slow. */ typedef pair< Song *, RString > val; vector vals; vals.reserve( vpSongsInOut.size() ); @@ -472,10 +476,7 @@ void SongUtil::SortSongPointerArrayByGrades( vector &vpSongsInOut, bool b int iCounts[NUM_Grade]; const Profile *pProfile = PROFILEMAN->GetMachineProfile(); - ASSERT( pProfile ); // XXX: Debugging. - const Style *pStyle = GAMESTATE->GetCurrentStyle(); - ASSERT( pStyle ); // XXX: Debugging. - StepsType st = pStyle->m_StepsType; + ASSERT( pProfile ); pProfile->GetGrades( pSong, st, iCounts ); RString foo; @@ -620,8 +621,12 @@ RString SongUtil::GetSectionNameFromSongAndSort( const Song* pSong, SortOrder so return RString(); case SORT_TOP_GRADES: { + StepsType st; + Difficulty dc; + SongUtil::GetStepsTypeAndDifficultyFromSortOrder( so, st, dc ); + int iCounts[NUM_Grade]; - PROFILEMAN->GetMachineProfile()->GetGrades( pSong, GAMESTATE->GetCurrentStyle()->m_StepsType, iCounts ); + PROFILEMAN->GetMachineProfile()->GetGrades( pSong, st, iCounts ); for( int i=Grade_Tier01; iGetCurrentStyle()->m_StepsType; + bDoubles = false; break; case SORT_DOUBLE_EASY_METER: case SORT_DOUBLE_MEDIUM_METER: case SORT_DOUBLE_HARD_METER: case SORT_DOUBLE_CHALLENGE_METER: - stOut = GAMESTATE->GetCurrentStyle()->m_StepsType; // in case we don't find any matches below - vector vpStyles; - GAMEMAN->GetStylesForGame(GAMESTATE->m_pCurGame,vpStyles); - FOREACH_CONST( const Style*, vpStyles, i ) - { - if( (*i)->m_StyleType == StyleType_OnePlayerTwoSides ) - { - // Ugly hack to ignore pump's half-double. - bool bContainsHalf = ((RString)(*i)->m_szName).find("half") != RString::npos; - if( bContainsHalf ) - continue; - stOut = (*i)->m_StepsType; - break; - } - } + bDoubles = true; + break; + } + + stOut = StepsType_Invalid; + + vector vpStyles; + GAMEMAN->GetStylesForGame(GAMESTATE->m_pCurGame,vpStyles); + FOREACH_CONST( const Style*, vpStyles, i ) + { + if( !(*i)->m_bUsedForGameplay ) + continue; + + if( bDoubles && (*i)->m_StyleType != StyleType_OnePlayerTwoSides ) + continue; + if( !bDoubles && (*i)->m_StyleType != StyleType_OnePlayerOneSide && (*i)->m_StyleType != StyleType_TwoPlayersTwoSides ) + continue; + + // Ugly hack to ignore pump's half-double. + bool bContainsHalf = ((RString)(*i)->m_szName).find("half") != RString::npos; + if( bContainsHalf ) + continue; + + stOut = (*i)->m_StepsType; break; }