diff --git a/stepmania/src/Course.cpp b/stepmania/src/Course.cpp index d38bb8a4bc..cca5e6cf8b 100644 --- a/stepmania/src/Course.cpp +++ b/stepmania/src/Course.cpp @@ -375,60 +375,39 @@ bool Course::GetTrailSorted( StepsType st, CourseDifficulty cd, Trail &trail ) c } // TODO: Move Course initialization after PROFILEMAN is created -static void CourseSortSongs( SongSort sort, vector &vpPossibleSongs, RandomGen &rnd ) +static void CourseSortSongs( SongSort sort, vector &vPossible, RandomGen &rnd ) { switch( sort ) { - default: - ASSERT(0); + DEFAULT_FAIL(sort); case SongSort_Randomize: - random_shuffle( vpPossibleSongs.begin(), vpPossibleSongs.end(), rnd ); + random_shuffle( vPossible.begin(), vPossible.end(), rnd ); break; case SongSort_MostPlays: - if( PROFILEMAN ) - SongUtil::SortSongPointerArrayByNumPlays( vpPossibleSongs, PROFILEMAN->GetMachineProfile(), true ); // descending + ASSERT(0); + // TODO: fix + //if( PROFILEMAN ) + // SongUtil::SortSongPointerArrayByNumPlays( vpPossibleSongs, PROFILEMAN->GetMachineProfile(), true ); // descending break; case SongSort_FewestPlays: - if( PROFILEMAN ) - SongUtil::SortSongPointerArrayByNumPlays( vpPossibleSongs, PROFILEMAN->GetMachineProfile(), false ); // ascending + ASSERT(0); + // TODO: fix + //if( PROFILEMAN ) + // SongUtil::SortSongPointerArrayByNumPlays( vpPossibleSongs, PROFILEMAN->GetMachineProfile(), false ); // ascending break; case SongSort_TopGrades: - SongUtil::SortSongPointerArrayByGrades( vpPossibleSongs, true ); // descending + ASSERT(0); + // TODO: fix + //SongUtil::SortSongPointerArrayByGrades( vpPossibleSongs, true ); // descending break; case SongSort_LowestGrades: - SongUtil::SortSongPointerArrayByGrades( vpPossibleSongs, false ); // ascending + ASSERT(0); + // TODO: fix + //SongUtil::SortSongPointerArrayByGrades( vpPossibleSongs, false ); // ascending break; } } -static void GetSongsValidForRandom( vector &vpSongsOut ) -{ - const vector &vpAllSongs = SONGMAN->GetAllSongs(); - - FOREACH_CONST( Song*, vpAllSongs, song ) - { - // Ignore locked songs when choosing randomly - // TODO: Move Course initialization after UNLOCKMAN is created - if( UNLOCKMAN && UNLOCKMAN->SongIsLocked(*song) ) - continue; - - // Ignore boring tutorial songs - if( (*song)->IsTutorial() ) - continue; - - // Don't allow long songs - if( SONGMAN->GetNumStagesForSong(*song) > 1 ) - continue; - - vpSongsOut.push_back( *song ); - } -} - -static bool StepsIsLocked( const Song *pSong, const Steps *pSteps ) -{ - return UNLOCKMAN->StepsIsLocked( pSong, pSteps ); -} - bool Course::GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail ) const { trail.Init(); @@ -469,102 +448,72 @@ bool Course::GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail ) bool bCourseDifficultyIsSignificant = (cd == DIFFICULTY_MEDIUM); vector vpAllPossibleSongs; - bool bHaveAllSongs = false; // true if vpAllPossibleSongs has been generated // Resolve each entry to a Song and Steps. FOREACH_CONST( CourseEntry, entries, e ) { - Song* pResolvedSong = NULL; // fill this in - Steps* pResolvedSteps = NULL; // fill this in + SongAndSteps resolved = { NULL, NULL }; // fill this in - // - // Create a list of matching songs. - // - - // Start with all songs - vector vpPossibleSteps; + SongCriteria soc = e->songCriteria; if( e->pSong ) { - if( IsAnEdit() && UNLOCKMAN->SongIsLocked(e->pSong) ) - continue; - // Choose an exact song, if we have matching steps and all - SongUtil::GetSteps( e->pSong, vpPossibleSteps, st, e->stepsCriteria.m_difficulty, e->stepsCriteria.m_iLowMeter, e->stepsCriteria.m_iHighMeter ); - // Remove all locked steps. - if( IsAnEdit() ) - RemoveIf( vpPossibleSteps, bind1st(ptr_fun(StepsIsLocked), e->pSong) ); - if( !vpPossibleSteps.empty() ) - pResolvedSong = e->pSong; - else - continue; + soc.m_bUseSongAllowedList = true; + soc.m_vpSongAllowedList.push_back( e->pSong ); } - else + soc.m_Tutorial = SongCriteria::Tutorial_No; + soc.m_Locked = SongCriteria::Locked_Unlocked; + soc.m_Locked = SongCriteria::Locked_Unlocked; + if( !soc.m_bUseSongAllowedList ) + soc.m_iStagesForSong = 1; + + StepsCriteria stc = e->stepsCriteria; + stc.m_st = st; + stc.m_Locked = StepsCriteria::Locked_Unlocked; + + vector vSongAndSteps; + StepsUtil::GetAllMatching( soc, stc, vSongAndSteps ); + + // It looks bad to have the same song 2x in a row in a randomly generated course. + // Don't allow the same song to be played 2x in a row, unless there's only + // one song in vpPossibleSongs. + if( trail.m_vEntries.size() > 0 && vSongAndSteps.size() > 1 ) { - /* Generate vpAllPossibleSongs, if we havn't yet. */ - if( !bHaveAllSongs ) + const TrailEntry &teLast = trail.m_vEntries[trail.m_vEntries.size()-1]; + bool bExistsDifferentSongThanLast = false; + FOREACH_CONST( SongAndSteps, vSongAndSteps, sas ) { - GetSongsValidForRandom( vpAllPossibleSongs ); - bHaveAllSongs = true; - } - - // copy over any songs that match our group filter, if one is set, and match our - // steps filter. - vector vpPossibleSongs; - FOREACH( Song*, vpAllPossibleSongs, song ) - { - if( !e->songCriteria.Matches( *song ) ) - continue; - - vector vpMatchingSteps; - SongUtil::GetSteps( *song, vpMatchingSteps, st, e->stepsCriteria.m_difficulty, e->stepsCriteria.m_iLowMeter, e->stepsCriteria.m_iHighMeter ); - StepsUtil::RemoveLockedSteps( *song, vpMatchingSteps ); - - if( vpMatchingSteps.empty() ) - continue; - - vpPossibleSongs.push_back( *song ); - } - - // It looks bad to have the same song 2x in a row in a randomly generated course. - // Don't allow the same song to be played 2x in a row, unless there's only - // one song in vpPossibleSongs. - if( trail.m_vEntries.size() > 0 && vpPossibleSongs.size() > 1 ) - { - const TrailEntry &teLast = trail.m_vEntries[trail.m_vEntries.size()-1]; - for( int i=vpPossibleSongs.size()-1; i>=0; i-- ) + if( sas->pSong != teLast.pSong ) { - if( vpPossibleSongs[i] == teLast.pSong ) - vpPossibleSongs.erase( vpPossibleSongs.begin()+i ); + bExistsDifferentSongThanLast = true; + break; } } - // if there are no songs to choose from, abort now - if( vpPossibleSongs.empty() ) - continue; - - CourseSortSongs( e->songSort, vpPossibleSongs, rnd ); - - ASSERT( e->iChooseIndex >= 0 ); - if( e->iChooseIndex < int(vpPossibleSongs.size()) ) - pResolvedSong = vpPossibleSongs[e->iChooseIndex]; - else - continue; - SongUtil::GetSteps( pResolvedSong, vpPossibleSteps, st, e->stepsCriteria.m_difficulty, e->stepsCriteria.m_iLowMeter, e->stepsCriteria.m_iHighMeter ); - StepsUtil::RemoveLockedSteps( pResolvedSong, vpPossibleSteps ); + for( int i=vSongAndSteps.size()-1; i>=0; i-- ) + { + if( vSongAndSteps[i].pSong == teLast.pSong ) + vSongAndSteps.erase( vSongAndSteps.begin()+i ); + } } - ASSERT( !vpPossibleSteps.empty() ); // if no steps are playable, this shouldn't be a possible song - random_shuffle( vpPossibleSteps.begin(), vpPossibleSteps.end(), rnd ); - pResolvedSteps = vpPossibleSteps[0]; - - if( pResolvedSong == NULL || pResolvedSteps == NULL ) - continue; // this song entry isn't playable. Skip. + // if there are no songs to choose from, abort this CourseEntry + if( vSongAndSteps.empty() ) + continue; + // TODO: fixme + CourseSortSongs( e->songSort, vSongAndSteps, rnd ); + + ASSERT( e->iChooseIndex >= 0 ); + if( e->iChooseIndex < int(vSongAndSteps.size()) ) + resolved = vSongAndSteps[e->iChooseIndex]; + else + continue; /* If we're not COURSE_DIFFICULTY_REGULAR, then we should be choosing steps that are * either easier or harder than the base difficulty. If no such steps exist, then * just use the one we already have. */ - Difficulty dc = pResolvedSteps->GetDifficulty(); + Difficulty dc = resolved.pSteps->GetDifficulty(); int iLowMeter = e->stepsCriteria.m_iLowMeter; int iHighMeter = e->stepsCriteria.m_iHighMeter; if( cd != DIFFICULTY_MEDIUM && !e->bNoDifficult ) @@ -575,11 +524,11 @@ bool Course::GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail ) bool bChangedDifficulty = false; if( new_dc != dc ) { - Steps* pNewSteps = SongUtil::GetStepsByDifficulty( pResolvedSong, st, new_dc ); + Steps* pNewSteps = SongUtil::GetStepsByDifficulty( resolved.pSong, st, new_dc ); if( pNewSteps ) { dc = new_dc; - pResolvedSteps = pNewSteps; + resolved.pSteps = pNewSteps; bChangedDifficulty = true; bCourseDifficultyIsSignificant = true; } @@ -596,8 +545,8 @@ bool Course::GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail ) { /* Minimum and maximum to add to make the meter range contain the actual * meter: */ - int iMinDist = pResolvedSteps->GetMeter() - iHighMeter; - int iMaxDist = pResolvedSteps->GetMeter() - iLowMeter; + int iMinDist = resolved.pSteps->GetMeter() - iHighMeter; + int iMaxDist = resolved.pSteps->GetMeter() - iLowMeter; /* Clamp the possible adjustments to try to avoid going under 1 or over * MAX_BOTTOM_RANGE. */ @@ -615,8 +564,8 @@ bool Course::GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail ) } TrailEntry te; - te.pSong = pResolvedSong; - te.pSteps = pResolvedSteps; + te.pSong = resolved.pSong; + te.pSteps = resolved.pSteps; te.Modifiers = e->sModifiers; te.Attacks = e->attacks; te.bSecret = e->bSecret; diff --git a/stepmania/src/SongUtil.cpp b/stepmania/src/SongUtil.cpp index d8fdf72366..5f8ab99d86 100644 --- a/stepmania/src/SongUtil.cpp +++ b/stepmania/src/SongUtil.cpp @@ -15,12 +15,62 @@ #include "LocalizedString.h" #include "RageLog.h" -bool SongCriteria::Matches( const Song *p ) const +bool SongCriteria::Matches( const Song *pSong ) const { - if( !m_sGroupName.empty() && m_sGroupName != p->m_sGroupName ) + if( !m_sGroupName.empty() && m_sGroupName != pSong->m_sGroupName ) return false; - if( !m_sGenre.empty() && m_sGenre != p->m_sGenre ) + if( !m_sGenre.empty() && m_sGenre != pSong->m_sGenre ) return false; + switch( m_Selectable ) + { + DEFAULT_FAIL(m_Selectable); + case Selectable_Yes: + if( pSong->m_SelectionDisplay != Song::SHOW_ALWAYS ) + return false; + break; + case Selectable_No: + if( pSong->m_SelectionDisplay != Song::SHOW_NEVER ) + return false; + break; + case Selectable_DontCare: + break; + } + if( m_bUseSongAllowedList ) + { + if( find(m_vpSongAllowedList.begin(),m_vpSongAllowedList.end(),pSong) == m_vpSongAllowedList.end() ) + return false; + } + if( m_iStagesForSong != -1 && SONGMAN->GetNumStagesForSong(pSong) != m_iStagesForSong ) + return false; + switch( m_Tutorial ) + { + DEFAULT_FAIL(m_Tutorial); + case Tutorial_Yes: + if( !pSong->IsTutorial() ) + return false; + break; + case Tutorial_No: + if( pSong->IsTutorial() ) + return false; + break; + case Tutorial_DontCare: + break; + } + switch( m_Locked ) + { + DEFAULT_FAIL(m_Locked); + case Locked_Locked: + if( UNLOCKMAN && !UNLOCKMAN->SongIsLocked(pSong) ) + return false; + break; + case Locked_Unlocked: + if( UNLOCKMAN && UNLOCKMAN->SongIsLocked(pSong) ) + return false; + break; + case Locked_DontCare: + break; + } + return true; } @@ -736,6 +786,17 @@ void SongUtil::GetAllSongGenres( vector &vsOut ) } } +void SongUtil::FilterSongs( const SongCriteria &sc, const vector &in, vector &out ) +{ + FOREACH_CONST( Song*, in, s ) + { + if( sc.Matches( *s ) ) + { + out.push_back( *s ); + } + } +} + ////////////////////////////////// // SongID diff --git a/stepmania/src/SongUtil.h b/stepmania/src/SongUtil.h index 2b3bfaeab9..b547980791 100644 --- a/stepmania/src/SongUtil.h +++ b/stepmania/src/SongUtil.h @@ -11,17 +11,27 @@ class Steps; class Profile; class XNode; - class SongCriteria { public: RString m_sGroupName; // "" means don't match RString m_sGenre; // "" means don't match + enum Selectable { Selectable_Yes, Selectable_No, Selectable_DontCare } m_Selectable; + bool m_bUseSongAllowedList; + vector m_vpSongAllowedList; + int m_iStagesForSong; // don't filter if -1 + enum Tutorial { Tutorial_Yes, Tutorial_No, Tutorial_DontCare } m_Tutorial; + enum Locked { Locked_Locked, Locked_Unlocked, Locked_DontCare } m_Locked; SongCriteria() { - + m_Selectable = Selectable_DontCare; + m_bUseSongAllowedList = false; + m_iStagesForSong = -1; + m_Tutorial = Tutorial_DontCare; + m_Locked = Locked_DontCare; } + bool Matches( const Song *p ) const; }; @@ -96,6 +106,10 @@ public: { return sDir < other.sDir; } + bool operator==( const SongID &other ) const + { + return sDir == other.sDir; + } XNode* CreateNode() const; void LoadFromNode( const XNode* pNode ); diff --git a/stepmania/src/StepsUtil.cpp b/stepmania/src/StepsUtil.cpp index caf327509e..edc81f2c64 100644 --- a/stepmania/src/StepsUtil.cpp +++ b/stepmania/src/StepsUtil.cpp @@ -11,14 +11,31 @@ #include "SongUtil.h" -bool StepsCriteria::Matches( const Steps *p ) const +bool StepsCriteria::Matches( const Song *pSong, const Steps *pSteps ) const { - if( m_difficulty != DIFFICULTY_INVALID && p->GetDifficulty() != m_difficulty ) + if( m_difficulty != DIFFICULTY_INVALID && pSteps->GetDifficulty() != m_difficulty ) return false; - if( m_iLowMeter != -1 && p->GetMeter() < m_iLowMeter ) + if( m_iLowMeter != -1 && pSteps->GetMeter() < m_iLowMeter ) return false; - if( m_iHighMeter != -1 && p->GetMeter() > m_iHighMeter ) + if( m_iHighMeter != -1 && pSteps->GetMeter() > m_iHighMeter ) return false; + if( m_st != STEPS_TYPE_INVALID && pSteps->m_StepsType != m_st ) + return false; + switch( m_Locked ) + { + DEFAULT_FAIL(m_Locked); + case Locked_Locked: + if( UNLOCKMAN && !UNLOCKMAN->StepsIsLocked(pSong,pSteps) ) + return false; + break; + case Locked_Unlocked: + if( UNLOCKMAN && UNLOCKMAN->StepsIsLocked(pSong,pSteps) ) + return false; + break; + case Locked_DontCare: + break; + } + return true; } @@ -30,9 +47,10 @@ void StepsUtil::GetAllMatching( const SongCriteria &soc, const StepsCriteria &st continue; FOREACH_CONST( Steps*, (*so)->GetAllSteps(), st ) { - if( !stc.Matches(*st) ) + if( !stc.Matches(*so,*st) ) continue; - + SongAndSteps sas = { *so, *st }; + out.push_back( sas ); } } } diff --git a/stepmania/src/StepsUtil.h b/stepmania/src/StepsUtil.h index 1692693cad..caff70f156 100644 --- a/stepmania/src/StepsUtil.h +++ b/stepmania/src/StepsUtil.h @@ -16,15 +16,19 @@ public: Difficulty m_difficulty; // don't filter if DIFFICULTY_INVALID int m_iLowMeter; // don't filter if -1 int m_iHighMeter; // don't filter if -1 + StepsType m_st; // don't filter if STEPS_TYPE_INVALID + enum Locked { Locked_Locked, Locked_Unlocked, Locked_DontCare } m_Locked; StepsCriteria() { m_difficulty = DIFFICULTY_INVALID; m_iLowMeter = -1; m_iHighMeter = -1; + m_st = STEPS_TYPE_INVALID; + m_Locked = Locked_DontCare; } - bool Matches( const Steps *p ) const; + bool Matches( const Song *pSong, const Steps *pSteps ) const; }; class SongAndSteps @@ -38,7 +42,6 @@ public: namespace StepsUtil { - void GetAllMatching( const SongCriteria &soc, const StepsCriteria &stc, vector &out ); // look up in SONGMAN bool CompareNotesPointersByRadarValues(const Steps* pSteps1, const Steps* pSteps2);