use SongCriteria and StepsCriteria to resolve songs for Trail

This commit is contained in:
Chris Danford
2006-07-02 06:04:53 +00:00
parent c2e8e70cf7
commit 97d28100ae
5 changed files with 176 additions and 131 deletions
+67 -118
View File
@@ -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;
+64 -3
View File
@@ -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
+16 -2
View File
@@ -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 );
+24 -6
View File
@@ -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 );
}
}
}
+5 -2
View File
@@ -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);