use SongCriteria and StepsCriteria to resolve songs for Trail
This commit is contained in:
+67
-118
@@ -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<Song*> &vpPossibleSongs, RandomGen &rnd )
|
||||
static void CourseSortSongs( SongSort sort, vector<SongAndSteps> &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<Song*> &vpSongsOut )
|
||||
{
|
||||
const vector<Song*> &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<Song*> 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<Steps*> 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<SongAndSteps> 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<Song*> vpPossibleSongs;
|
||||
FOREACH( Song*, vpAllPossibleSongs, song )
|
||||
{
|
||||
if( !e->songCriteria.Matches( *song ) )
|
||||
continue;
|
||||
|
||||
vector<Steps*> 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;
|
||||
|
||||
@@ -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<RString> &vsOut )
|
||||
}
|
||||
}
|
||||
|
||||
void SongUtil::FilterSongs( const SongCriteria &sc, const vector<Song*> &in, vector<Song*> &out )
|
||||
{
|
||||
FOREACH_CONST( Song*, in, s )
|
||||
{
|
||||
if( sc.Matches( *s ) )
|
||||
{
|
||||
out.push_back( *s );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////
|
||||
// SongID
|
||||
|
||||
@@ -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<Song*> 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 );
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<SongAndSteps> &out ); // look up in SONGMAN
|
||||
|
||||
bool CompareNotesPointersByRadarValues(const Steps* pSteps1, const Steps* pSteps2);
|
||||
|
||||
Reference in New Issue
Block a user