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_bRepeat = false;
|
||||||
m_bRandomize = false;
|
m_bRandomize = false;
|
||||||
m_iLives = -1;
|
m_iLives = -1;
|
||||||
|
m_bSortByMeter = false;
|
||||||
ZERO( m_iCustomMeter );
|
ZERO( m_iCustomMeter );
|
||||||
m_entries.clear();
|
m_entries.clear();
|
||||||
m_sPath = m_sName = m_sTranslitName = m_sBannerPath = m_sCDTitlePath = "";
|
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();
|
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:
|
* Difficult courses do the following:
|
||||||
*
|
*
|
||||||
@@ -501,6 +543,13 @@ static vector<Song*> GetFilteredBestSongs( StepsType st )
|
|||||||
return ret;
|
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
|
/* 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
|
* be called on all songs to sort. It can take time to execute, so we cache the
|
||||||
* results. */
|
* results. */
|
||||||
@@ -516,7 +565,44 @@ Trail* Course::GetTrail( StepsType st, CourseDifficulty cd ) const
|
|||||||
return &it->second;
|
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.
|
// 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 */
|
int CurSong = 0; /* Current offset into AllSongsShuffled */
|
||||||
|
|
||||||
Trail trail;
|
|
||||||
trail.m_StepsType = st;
|
trail.m_StepsType = st;
|
||||||
trail.m_CourseDifficulty = cd;
|
trail.m_CourseDifficulty = cd;
|
||||||
|
|
||||||
@@ -680,10 +765,6 @@ Trail* Course::GetTrail( StepsType st, CourseDifficulty cd ) const
|
|||||||
te.iHighMeter = high_meter;
|
te.iHighMeter = high_meter;
|
||||||
trail.m_vEntries.push_back( te );
|
trail.m_vEntries.push_back( te );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cache results. */
|
|
||||||
m_TrailCache[params] = trail;
|
|
||||||
return &m_TrailCache[params];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Course::HasMods() const
|
bool Course::HasMods() const
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ public:
|
|||||||
bool m_bRandomize; // play the songs in a random order
|
bool m_bRandomize; // play the songs in a random order
|
||||||
int m_iLives; // -1 means use bar life meter
|
int m_iLives; // -1 means use bar life meter
|
||||||
int m_iCustomMeter[NUM_COURSE_DIFFICULTIES]; // -1 = no meter specified
|
int m_iCustomMeter[NUM_COURSE_DIFFICULTIES]; // -1 = no meter specified
|
||||||
|
bool m_bSortByMeter;
|
||||||
|
|
||||||
vector<CourseEntry> m_entries;
|
vector<CourseEntry> m_entries;
|
||||||
|
|
||||||
@@ -122,8 +123,9 @@ public:
|
|||||||
void LoadFromCRSFile( CString sPath );
|
void LoadFromCRSFile( CString sPath );
|
||||||
void Init();
|
void Init();
|
||||||
void Save();
|
void Save();
|
||||||
void AutogenEndlessFromGroup( CString sGroupName, Difficulty diff );
|
void AutogenEndlessFromGroup( CString sGroupName, Difficulty dc );
|
||||||
void AutogenNonstopFromGroup( CString sGroupName, Difficulty diff );
|
void AutogenNonstopFromGroup( CString sGroupName, Difficulty dc );
|
||||||
|
void AutogenOniFromArtist( CString sArtistName, vector<Song*> aSongs, Difficulty dc );
|
||||||
|
|
||||||
// sorting values
|
// sorting values
|
||||||
int m_SortOrder_TotalDifficulty;
|
int m_SortOrder_TotalDifficulty;
|
||||||
@@ -136,6 +138,7 @@ public:
|
|||||||
void ClearCache();
|
void ClearCache();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail ) const;
|
||||||
void GetMeterRange( int stage, int& iMeterLowOut, int& iMeterHighOut, CourseDifficulty cd ) const;
|
void GetMeterRange( int stage, int& iMeterLowOut, int& iMeterHighOut, CourseDifficulty cd ) const;
|
||||||
|
|
||||||
typedef pair<StepsType,CourseDifficulty> TrailParams;
|
typedef pair<StepsType,CourseDifficulty> TrailParams;
|
||||||
|
|||||||
@@ -589,6 +589,47 @@ void SongManager::InitAutogenCourses()
|
|||||||
pCourse = new Course;
|
pCourse = new Course;
|
||||||
pCourse->AutogenEndlessFromGroup( "", DIFFICULTY_MEDIUM );
|
pCourse->AutogenEndlessFromGroup( "", DIFFICULTY_MEDIUM );
|
||||||
m_pCourses.push_back( pCourse );
|
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