refactor Course: move some responsibilities to Trail

This commit is contained in:
Chris Danford
2004-05-23 00:53:20 +00:00
parent 6ac93f463a
commit abfc956e4f
30 changed files with 479 additions and 472 deletions
+72 -193
View File
@@ -15,6 +15,7 @@
#include "song.h"
#include "GameManager.h"
#include "SongManager.h"
#include "GameState.h"
#include "RageException.h"
#include "RageLog.h"
#include "MsdFile.h"
@@ -23,7 +24,6 @@
#include "RageUtil.h"
#include "TitleSubstitution.h"
#include "Steps.h"
#include "GameState.h"
#include "BannerCache.h"
#include "RageFile.h"
#include "arch/arch.h"
@@ -40,7 +40,7 @@ const int MAX_BOTTOM_RANGE = 10;
Course::Course()
{
Unload();
Init();
}
PlayMode Course::GetPlayMode() const
@@ -50,60 +50,11 @@ PlayMode Course::GetPlayMode() const
return m_iLives > 0? PLAY_MODE_ONI:PLAY_MODE_NONSTOP;
}
float Course::GetMeterForPlayer( PlayerNumber pn ) const
{
return GetMeter( GAMESTATE->m_PreferredCourseDifficulty[pn] );
}
float Course::GetMeter( CourseDifficulty cd ) const
{
/* If we have a meter for this difficulty, use it. */
if( m_iMeter[cd] != -1 )
return float( m_iMeter[cd] );
/*LOG->Trace( "Course file '%s' contains a song '%s%s%s' that is not present",
m_sPath.c_str(), sGroup.c_str(), sGroup.size()? "/":"", sSong.c_str());*/
vector<Info> ci;
GetCourseInfo( GAMESTATE->GetCurrentStyleDef()->m_StepsType, ci, cd );
if( ci.size() == 0 )
return 0;
/* Take the average meter. */
float fTotalMeter = 0;
for( unsigned c = 0; c < ci.size(); ++c )
{
if( ci[c].Mystery )
{
switch( GetDifficulty(ci[c]) )
{
case DIFFICULTY_INVALID:
{
int iMeterLow, iMeterHigh;
GetMeterRange(ci[c], iMeterLow, iMeterHigh );
fTotalMeter += (iMeterLow + iMeterHigh) / 2.0f;
break;
}
/* XXX? */
case DIFFICULTY_BEGINNER: fTotalMeter += 1; break;
case DIFFICULTY_EASY: fTotalMeter += 2; break;
case DIFFICULTY_MEDIUM: fTotalMeter += 5; break;
case DIFFICULTY_HARD: fTotalMeter += 7; break;
case DIFFICULTY_CHALLENGE: fTotalMeter += 9; break;
}
}
else
fTotalMeter += ci[c].pNotes->GetMeter();
}
// LOG->Trace("Course '%s': %f", m_sName.c_str(), fTotalMeter/ci.size() );
return fTotalMeter / ci.size();
}
void Course::LoadFromCRSFile( CString sPath )
{
LOG->Trace( "Course::LoadFromCRSFile( '%s' )", sPath.c_str() );
Unload();
Init();
m_sPath = sPath; // save path
@@ -152,7 +103,7 @@ void Course::LoadFromCRSFile( CString sPath )
else if( 0 == stricmp(sValueName, "METER") )
{
if( sParams.params.size() == 2 )
m_iMeter[COURSE_DIFFICULTY_REGULAR] = atoi( sParams[1] ); /* compat */
m_iCustomMeter[COURSE_DIFFICULTY_REGULAR] = atoi( sParams[1] ); /* compat */
else if( sParams.params.size() == 3 )
{
const CourseDifficulty cd = StringToCourseDifficulty( sParams[1] );
@@ -162,7 +113,7 @@ void Course::LoadFromCRSFile( CString sPath )
m_sPath.c_str(), sParams[1].c_str() );
continue;
}
m_iMeter[cd] = atoi( sParams[2] );
m_iCustomMeter[cd] = atoi( sParams[2] );
}
}
@@ -314,13 +265,13 @@ void Course::LoadFromCRSFile( CString sPath )
ignore, ignore, ignore);
}
void Course::Unload()
void Course::Init()
{
m_bIsAutogen = false;
m_bRepeat = false;
m_bRandomize = false;
m_iLives = -1;
m_iMeter[0] = m_iMeter[1] = -1;
ZERO( m_iCustomMeter );
m_entries.clear();
m_sPath = m_sName = m_sTranslitName = m_sBannerPath = m_sCDTitlePath = "";
}
@@ -343,9 +294,9 @@ void Course::Save()
f.PutLine( ssprintf("#LIVES:%i;", m_iLives) );
FOREACH_CourseDifficulty( cd )
{
if( m_iMeter[cd] == -1 )
if( m_iCustomMeter[cd] == -1 )
continue;
f.PutLine( ssprintf("#METER:%s:%i;", CourseDifficultyToString(cd).c_str(), m_iMeter[cd]) );
f.PutLine( ssprintf("#METER:%s:%i;", CourseDifficultyToString(cd).c_str(), m_iCustomMeter[cd]) );
}
for( unsigned i=0; i<m_entries.size(); i++ )
@@ -435,7 +386,7 @@ void Course::AutogenEndlessFromGroup( CString sGroupName, Difficulty diff )
m_bRepeat = true;
m_bRandomize = true;
m_iLives = -1;
m_iMeter[0] = m_iMeter[1] = -1;
m_iCustomMeter[0] = m_iCustomMeter[1] = -1;
if( sGroupName == "" )
{
@@ -502,58 +453,20 @@ bool Course::HasCourseDifficulty( StepsType nt, CourseDifficulty cd ) const
{
/* Check to see if any songs would change if difficult. */
/* COURSE_DIFFICULTY_REGULAR is always available (provided IsPlayableIn). */
/* COURSE_DIFFICULTY_REGULAR is always available (if IsPlayableIn == true). */
if( cd == COURSE_DIFFICULTY_REGULAR )
return true;
vector<Info> Normal, Hard;
GetCourseInfo( nt, Normal, COURSE_DIFFICULTY_REGULAR );
GetCourseInfo( nt, Hard, cd );
Trail *Regular = GetTrail( nt, COURSE_DIFFICULTY_REGULAR );
Trail *Other = GetTrail( nt, cd );
if( Normal.size() != Hard.size() )
return true; /* it changed */
for( unsigned i=0; i<Normal.size(); i++ )
{
if( Normal[i].CourseIndex != Hard[i].CourseIndex )
return true; /* it changed */
if( Normal[i].Mystery )
{
const CourseEntry &e = m_entries[ Normal[i].CourseIndex ];
if( e.difficulty != DIFFICULTY_INVALID )
{
/* Check if the song's difficulty class will actually change. */
Difficulty dc = (Difficulty) (e.difficulty + COURSE_DIFFICULTY_CLASS_CHANGE[cd]);
dc = clamp( dc, DIFFICULTY_BEGINNER, DIFFICULTY_CHALLENGE );
if( dc != e.difficulty )
return true;
}
/* Meters under MAX_BOTTOM_RANGE..MAX_BOTTOM_RANGE change by getting harder. */
if( e.difficulty == DIFFICULTY_INVALID )
{
/* Check if the meter will actually change. */
int iNewLowMeter = clamp( 1, e.low_meter + COURSE_DIFFICULTY_METER_CHANGE[cd], MAX_BOTTOM_RANGE );
int iNewHighMeter = clamp( 1, e.high_meter + COURSE_DIFFICULTY_METER_CHANGE[cd], MAX_BOTTOM_RANGE );
if( iNewLowMeter != e.low_meter || iNewHighMeter != e.high_meter )
return true;
}
continue;
}
if( Normal[i].pSong != Hard[i].pSong || Normal[i].pNotes != Hard[i].pNotes )
return true;
}
return false;
return Regular != Other;
}
bool Course::IsPlayableIn( StepsType nt ) const
{
vector<Info> ci;
GetCourseInfo( nt, ci );
return ci.size() > 0;
Trail* pTrail = GetTrail( nt, COURSE_DIFFICULTY_REGULAR );
return !pTrail->m_vEntries.empty();
}
static vector<Song*> GetFilteredBestSongs( StepsType nt )
@@ -597,16 +510,22 @@ static vector<Song*> GetFilteredBestSongs( StepsType nt )
/* 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. */
void Course::GetCourseInfo( StepsType nt, vector<Course::Info> &ci, CourseDifficulty cd ) const
Trail* Course::GetTrail( StepsType nt, CourseDifficulty cd ) const
{
const InfoParams params( nt, cd );
InfoCache::const_iterator it = m_InfoCache.find( params );
if( it != m_InfoCache.end() )
//
// Look in the Trail cache
//
const TrailParams params( nt, cd );
TrailCache::iterator it = m_TrailCache.find( params );
if( it != m_TrailCache.end() )
{
ci = it->second;
return;
return &it->second;
}
//
// Construct a new Trail, add it to the cache, then return it.
//
/* Different seed for each course, but the same for the whole round: */
RandomGen rnd( GAMESTATE->m_iRoundSeed + GetHashForString(m_sName) );
@@ -630,7 +549,8 @@ void Course::GetCourseInfo( StepsType nt, vector<Course::Info> &ci, CourseDiffic
vector<Song*> AllSongsShuffled;
int CurSong = 0; /* Current offset into AllSongsShuffled */
ci.clear();
Trail trail;
for( unsigned i=0; i<entries.size(); i++ )
{
@@ -754,20 +674,20 @@ void Course::GetCourseInfo( StepsType nt, vector<Course::Info> &ci, CourseDiffic
}
}
Info cinfo;
cinfo.pSong = pSong;
cinfo.pNotes = pNotes;
cinfo.Modifiers = e.modifiers;
cinfo.Attacks = e.attacks;
cinfo.Random = ( e.type == COURSE_ENTRY_RANDOM || e.type == COURSE_ENTRY_RANDOM_WITHIN_GROUP );
cinfo.Mystery = e.mystery;
cinfo.CourseIndex = i;
cinfo.Difficulty = entry_difficulty;
ci.push_back( cinfo );
TrailEntry te;
te.pSong = pSong;
te.pNotes = pNotes;
te.Modifiers = e.modifiers;
te.Attacks = e.attacks;
te.bMystery = e.mystery;
te.iLowMeter = low_meter;
te.iHighMeter = high_meter;
trail.m_vEntries.push_back( te );
}
/* Cache results. */
m_InfoCache[params] = ci;
m_TrailCache[params] = trail;
return &m_TrailCache[params];
}
bool Course::HasMods() const
@@ -793,11 +713,14 @@ bool Course::AllSongsAreFixed() const
void Course::ClearCache()
{
m_InfoCache.clear();
m_TrailCache.clear();
}
RageColor Course::GetColor() const
{
// FIXME: These colors shouldn't be hard-coded
int iMeter = 5;
switch (PREFSMAN->m_iCourseSortOrder)
{
case PrefsManager::COURSE_SORT_SONGS:
@@ -813,31 +736,31 @@ RageColor Course::GetColor() const
case PrefsManager::COURSE_SORT_METER:
if ( !IsFixed() )
return RageColor(0,0,1,1); // blue
if (GetMeter() > 8.5)
if (iMeter > 8.5)
return RageColor(1,0,0,1); // red
if (GetMeter() >= 7)
if (iMeter >= 7)
return RageColor(1,0.5f,0,1); // orange
if (GetMeter() >= 5)
if (iMeter >= 5)
return RageColor(1,1,0,1); // yellow
return RageColor(0,1,0,1); // green
case PrefsManager::COURSE_SORT_METER_SUM:
if ( !IsFixed() )
return RageColor(0,0,1,1); // blue
if (SortOrder_TotalDifficulty >= 40)
if (m_SortOrder_TotalDifficulty >= 40)
return RageColor(1,0,0,1); // red
if (SortOrder_TotalDifficulty >= 30)
if (m_SortOrder_TotalDifficulty >= 30)
return RageColor(1,0.5f,0,1); // orange
if (SortOrder_TotalDifficulty >= 20)
if (m_SortOrder_TotalDifficulty >= 20)
return RageColor(1,1,0,1); // yellow
return RageColor(0,1,0,1); // green
case PrefsManager::COURSE_SORT_RANK:
if (SortOrder_Ranking == 3)
if (m_SortOrder_Ranking == 3)
return RageColor(0,0,1,1); // blue
if (SortOrder_Ranking == 2)
if (m_SortOrder_Ranking == 2)
return RageColor(1,0.5f,0,1); // orange
if (SortOrder_Ranking == 1)
if (m_SortOrder_Ranking == 1)
return RageColor(0,1,0,1); // green
return RageColor(1,1,0,1); // yellow, never should get here
default:
@@ -859,14 +782,6 @@ bool Course::IsFixed() const
return true;
}
Difficulty Course::GetDifficulty( const Info &stage ) const
{
Difficulty dc = m_entries[stage.CourseIndex].difficulty;
Difficulty new_dc = Difficulty( dc + COURSE_DIFFICULTY_CLASS_CHANGE[stage.Difficulty] );
new_dc = clamp( new_dc, DIFFICULTY_BEGINNER, DIFFICULTY_CHALLENGE );
return new_dc;
}
void Course::GetMeterRange( int stage, int& iMeterLowOut, int& iMeterHighOut, CourseDifficulty cd ) const
{
iMeterLowOut = m_entries[stage].low_meter;
@@ -878,23 +793,14 @@ void Course::GetMeterRange( int stage, int& iMeterLowOut, int& iMeterHighOut, Co
}
void Course::GetMeterRange( const Info &stage, int& iMeterLowOut, int& iMeterHighOut ) const
bool Course::GetTotalSeconds( StepsType st, float& fSecondsOut ) const
{
GetMeterRange( stage.CourseIndex, iMeterLowOut, iMeterHighOut, stage.Difficulty );
}
if( !IsFixed() )
return false;
bool Course::GetTotalSeconds( float& fSecondsOut ) const
{
vector<Info> ci;
GetCourseInfo( STEPS_TYPE_DANCE_SINGLE, ci );
Trail* pTrail = GetTrail( st, COURSE_DIFFICULTY_REGULAR );
fSecondsOut = 0;
for( unsigned i=0; i<ci.size(); i++ )
{
if( ci[i].Mystery )
return false;
fSecondsOut += ci[i].pSong->m_fMusicLengthSeconds;
}
fSecondsOut = pTrail->GetLengthSeconds();
return true;
}
@@ -918,9 +824,9 @@ bool Course::HasBanner() const
return m_sBannerPath != "" && IsAFile(m_sBannerPath);
}
void Course::UpdateCourseStats()
void Course::UpdateCourseStats( StepsType st )
{
SortOrder_TotalDifficulty = 0;
m_SortOrder_TotalDifficulty = 0;
unsigned i;
@@ -930,17 +836,15 @@ void Course::UpdateCourseStats()
if ( m_entries[i].type == COURSE_ENTRY_FIXED )
continue;
if ( SortOrder_Ranking == 2 )
SortOrder_Ranking = 3;
SortOrder_TotalDifficulty = 999999; // large number
if ( m_SortOrder_Ranking == 2 )
m_SortOrder_Ranking = 3;
m_SortOrder_TotalDifficulty = 999999; // large number
return;
}
vector<Info> ci;
GetCourseInfo( GAMESTATE->GetCurrentStyleDef()->m_StepsType, ci );
Trail* pTrail = GetTrail( st, COURSE_DIFFICULTY_REGULAR );
for( i = 0; i < ci.size(); i++ )
SortOrder_TotalDifficulty += ci[i].pNotes->GetMeter();
m_SortOrder_TotalDifficulty += pTrail->GetTotalMeter();
// OPTIMIZATION: Ranking info isn't dependant on style, so
// call it sparingly. Its handled on startup and when
@@ -948,23 +852,7 @@ void Course::UpdateCourseStats()
LOG->Trace("%s: Total feet: %d",
this->m_sName.c_str(),
SortOrder_TotalDifficulty );
}
void Course::Info::GetAttackArray( AttackArray &out ) const
{
if( !Modifiers.empty() )
{
Attack a;
a.fStartSecond = 0;
a.fSecsRemaining = 10000; /* whole song */
a.level = ATTACK_LEVEL_1;
a.sModifier = Modifiers;
out.push_back( a );
}
out.insert( out.end(), Attacks.begin(), Attacks.end() );
m_SortOrder_TotalDifficulty );
}
bool Course::IsRanking() const
@@ -980,20 +868,11 @@ bool Course::IsRanking() const
return false;
}
RadarValues Course::GetRadarValues( StepsType st, CourseDifficulty cd ) const
float Course::GetMeter( StepsType nt, CourseDifficulty cd ) const
{
RadarValues rv;
/* If we have a manually-entered meter for this difficulty, use it. */
if( m_iCustomMeter[cd] != -1 )
return m_iCustomMeter[cd];
vector<Course::Info> ci;
this->GetCourseInfo( st, ci, cd );
for( unsigned i = 0; i < ci.size(); ++i )
{
const Steps *pNotes = ci[i].pNotes;
ASSERT( pNotes );
rv += pNotes->GetRadarValues();
}
return rv;
return roundf( GetTrail(nt,cd)->GetAverageMeter() );
}