autogen Oni courses by artist, sorted by song difficulty
This commit is contained in:
@@ -272,6 +272,7 @@ void Course::Init()
|
||||
m_bRepeat = false;
|
||||
m_bRandomize = false;
|
||||
m_iLives = -1;
|
||||
m_bSortByMeter = false;
|
||||
ZERO( m_iCustomMeter );
|
||||
m_entries.clear();
|
||||
m_sPath = m_sName = m_sTranslitName = m_sBannerPath = m_sCDTitlePath = "";
|
||||
@@ -432,6 +433,47 @@ void Course::AutogenNonstopFromGroup( CString sGroupName, Difficulty diff )
|
||||
m_entries.pop_back();
|
||||
}
|
||||
|
||||
void Course::AutogenOniFromArtist( CString sArtistName, vector<Song*> aSongs, Difficulty dc )
|
||||
{
|
||||
m_bIsAutogen = true;
|
||||
m_bRepeat = false;
|
||||
m_bRandomize = true;
|
||||
m_bSortByMeter = true;
|
||||
|
||||
m_iLives = 4;
|
||||
m_iCustomMeter[0] = m_iCustomMeter[1] = -1;
|
||||
|
||||
ASSERT( sArtistName != "" );
|
||||
ASSERT( aSongs.size() > 0 );
|
||||
|
||||
/* "Artist Oni" is a little repetitive; "by Artist" stands out less, and lowercasing
|
||||
* "by" puts more emphasis on the artist's name. It also sorts them together. */
|
||||
m_sName = "by " + sArtistName;
|
||||
|
||||
// m_sBannerPath = ""; // XXX
|
||||
|
||||
/* Shuffle the list to determine which songs we'll use. Shuffle it deterministically,
|
||||
* so we always get the same set of songs unless the song set changes. */
|
||||
{
|
||||
RandomGen rng( GetHashForString( sArtistName ) + aSongs.size() );
|
||||
random_shuffle( aSongs.begin(), aSongs.end(), rng );
|
||||
}
|
||||
|
||||
/* Only use up to four songs. */
|
||||
if( aSongs.size() > 4 )
|
||||
aSongs.erase( aSongs.begin()+4, aSongs.end() );
|
||||
|
||||
CourseEntry e;
|
||||
e.type = COURSE_ENTRY_FIXED;
|
||||
e.difficulty = dc;
|
||||
|
||||
for( unsigned i = 0; i < aSongs.size(); ++i )
|
||||
{
|
||||
e.pSong = aSongs[i];
|
||||
m_entries.push_back( e );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Difficult courses do the following:
|
||||
*
|
||||
@@ -501,6 +543,13 @@ static vector<Song*> GetFilteredBestSongs( StepsType st )
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct SortTrailEntry
|
||||
{
|
||||
TrailEntry entry;
|
||||
int SortMeter;
|
||||
bool operator< ( const SortTrailEntry &rhs ) const { return SortMeter < rhs.SortMeter; }
|
||||
};
|
||||
|
||||
/* This is called by many simple functions, like Course::GetTotalSeconds, and may
|
||||
* be called on all songs to sort. It can take time to execute, so we cache the
|
||||
* results. */
|
||||
@@ -516,7 +565,44 @@ Trail* Course::GetTrail( StepsType st, CourseDifficulty cd ) const
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
//
|
||||
// Construct a new Trail, add it to the cache, then return it.
|
||||
//
|
||||
Trail trail;
|
||||
GetTrailUnsorted( st, cd, trail );
|
||||
|
||||
if( this->m_bSortByMeter )
|
||||
{
|
||||
/* Sort according to COURSE_DIFFICULTY_REGULAR, since the order of songs
|
||||
* must not change across difficulties. */
|
||||
Trail SortTrail;
|
||||
if( cd == COURSE_DIFFICULTY_REGULAR )
|
||||
SortTrail = trail;
|
||||
else
|
||||
GetTrailUnsorted( st, COURSE_DIFFICULTY_REGULAR, SortTrail );
|
||||
ASSERT_M( trail.m_vEntries.size() == SortTrail.m_vEntries.size(), ssprintf("%i %i", trail.m_vEntries.size(), SortTrail.m_vEntries.size()) );
|
||||
|
||||
vector<SortTrailEntry> entries;
|
||||
for( unsigned i = 0; i < trail.m_vEntries.size(); ++i )
|
||||
{
|
||||
SortTrailEntry ste;
|
||||
ste.entry = trail.m_vEntries[i];
|
||||
ste.SortMeter = SortTrail.m_vEntries[i].pSteps->GetMeter();
|
||||
entries.push_back( ste );
|
||||
}
|
||||
|
||||
stable_sort( entries.begin(), entries.end() );
|
||||
for( unsigned i = 0; i < trail.m_vEntries.size(); ++i )
|
||||
trail.m_vEntries[i] = entries[i].entry;
|
||||
}
|
||||
|
||||
/* Cache results. */
|
||||
m_TrailCache[params] = trail;
|
||||
return &m_TrailCache[params];
|
||||
}
|
||||
|
||||
void Course::GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail ) const
|
||||
{
|
||||
//
|
||||
// Construct a new Trail, add it to the cache, then return it.
|
||||
//
|
||||
@@ -544,7 +630,6 @@ Trail* Course::GetTrail( StepsType st, CourseDifficulty cd ) const
|
||||
|
||||
int CurSong = 0; /* Current offset into AllSongsShuffled */
|
||||
|
||||
Trail trail;
|
||||
trail.m_StepsType = st;
|
||||
trail.m_CourseDifficulty = cd;
|
||||
|
||||
@@ -680,10 +765,6 @@ Trail* Course::GetTrail( StepsType st, CourseDifficulty cd ) const
|
||||
te.iHighMeter = high_meter;
|
||||
trail.m_vEntries.push_back( te );
|
||||
}
|
||||
|
||||
/* Cache results. */
|
||||
m_TrailCache[params] = trail;
|
||||
return &m_TrailCache[params];
|
||||
}
|
||||
|
||||
bool Course::HasMods() const
|
||||
|
||||
@@ -95,6 +95,7 @@ public:
|
||||
bool m_bRandomize; // play the songs in a random order
|
||||
int m_iLives; // -1 means use bar life meter
|
||||
int m_iCustomMeter[NUM_COURSE_DIFFICULTIES]; // -1 = no meter specified
|
||||
bool m_bSortByMeter;
|
||||
|
||||
vector<CourseEntry> m_entries;
|
||||
|
||||
@@ -122,8 +123,9 @@ public:
|
||||
void LoadFromCRSFile( CString sPath );
|
||||
void Init();
|
||||
void Save();
|
||||
void AutogenEndlessFromGroup( CString sGroupName, Difficulty diff );
|
||||
void AutogenNonstopFromGroup( CString sGroupName, Difficulty diff );
|
||||
void AutogenEndlessFromGroup( CString sGroupName, Difficulty dc );
|
||||
void AutogenNonstopFromGroup( CString sGroupName, Difficulty dc );
|
||||
void AutogenOniFromArtist( CString sArtistName, vector<Song*> aSongs, Difficulty dc );
|
||||
|
||||
// sorting values
|
||||
int m_SortOrder_TotalDifficulty;
|
||||
@@ -136,6 +138,7 @@ public:
|
||||
void ClearCache();
|
||||
|
||||
private:
|
||||
void GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail ) const;
|
||||
void GetMeterRange( int stage, int& iMeterLowOut, int& iMeterHighOut, CourseDifficulty cd ) const;
|
||||
|
||||
typedef pair<StepsType,CourseDifficulty> TrailParams;
|
||||
|
||||
@@ -589,6 +589,47 @@ void SongManager::InitAutogenCourses()
|
||||
pCourse = new Course;
|
||||
pCourse->AutogenEndlessFromGroup( "", DIFFICULTY_MEDIUM );
|
||||
m_pCourses.push_back( pCourse );
|
||||
|
||||
/* Generate Oni courses from artists. Only create courses if we have at least
|
||||
* four songs from an artist; create 3- and 4-song courses. */
|
||||
vector<Song *> aSongs;
|
||||
{
|
||||
vector<Song*> apSongs = this->GetAllSongs();
|
||||
SongUtil::SortSongPointerArrayByArtist( apSongs );
|
||||
|
||||
CString sCurArtist = "";
|
||||
int iCurArtistCount = 0;
|
||||
|
||||
unsigned i = 0;
|
||||
do {
|
||||
CString sArtist = i >= apSongs.size()? "": apSongs[i]->GetTranslitArtist();
|
||||
if( i < apSongs.size() && !sCurArtist.CompareNoCase(sArtist) )
|
||||
{
|
||||
aSongs.push_back( apSongs[i] );
|
||||
++iCurArtistCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Different artist, or we're at the end. If we have enough entries for
|
||||
* the last artist, add it. Skip blanks and "Unknown artist". */
|
||||
if( iCurArtistCount >= 3 && sCurArtist != "" &&
|
||||
sCurArtist.CompareNoCase("Unknown artist") )
|
||||
{
|
||||
pCourse = new Course;
|
||||
pCourse->AutogenOniFromArtist( sCurArtist, aSongs, DIFFICULTY_HARD );
|
||||
m_pCourses.push_back( pCourse );
|
||||
}
|
||||
|
||||
aSongs.clear();
|
||||
|
||||
if( i < apSongs.size() )
|
||||
{
|
||||
sCurArtist = sArtist;
|
||||
iCurArtistCount = 1;
|
||||
aSongs.push_back( apSongs[i] );
|
||||
}
|
||||
} while( i++ < apSongs.size() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user