2002-06-14 22:25:22 +00:00
# include "stdafx.h"
/*
-----------------------------------------------------------------------------
Class: Course
Desc: See header.
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
Chris Danford
-----------------------------------------------------------------------------
*/
# include "Course.h"
2002-07-23 01:41:40 +00:00
# include "PrefsManager.h"
2002-07-02 17:34:20 +00:00
# include "Song.h"
2002-07-23 01:41:40 +00:00
# include "GameManager.h"
# include "SongManager.h"
# include "GameState.h"
2002-07-27 19:29:51 +00:00
# include "RageException.h"
# include "RageLog.h"
# include "MsdFile.h"
2002-07-28 20:28:37 +00:00
# include "PlayerOptions.h"
# include "SongOptions.h"
2002-11-11 04:53:31 +00:00
# include "RageUtil.h"
2003-02-10 23:17:57 +00:00
# include "TitleSubstitution.h"
2002-06-14 22:25:22 +00:00
2002-12-17 05:07:56 +00:00
Course : : Course ( )
{
m_bRepeat = false ;
m_bRandomize = false ;
2003-01-26 09:46:30 +00:00
m_iLives = - 1 ;
2002-12-17 05:07:56 +00:00
m_iExtra = 0 ;
2003-02-04 21:11:20 +00:00
m_iNumTimesPlayed = 0 ;
2003-01-22 05:29:27 +00:00
//
// Init high scores
//
2003-01-26 07:33:03 +00:00
unsigned i , j ;
for ( i = 0 ; i < NUM_NOTES_TYPES ; i + + )
2003-01-27 02:00:38 +00:00
for ( j = 0 ; j < NUM_RANKING_LINES ; j + + )
2003-01-26 07:33:03 +00:00
{
2003-02-12 23:11:37 +00:00
m_MachineScores [ i ] [ j ] . iDancePoints = 573 ;
m_MachineScores [ i ] [ j ] . fSurviveTime = 57.3f ;
2003-01-26 07:33:03 +00:00
m_MachineScores [ i ] [ j ] . sName = " STEP " ;
}
2003-01-22 05:29:27 +00:00
2003-01-26 07:33:03 +00:00
for ( i = 0 ; i < NUM_NOTES_TYPES ; i + + )
for ( j = 0 ; j < NUM_PLAYERS ; j + + )
{
m_MemCardScores [ i ] [ j ] . iDancePoints = 0 ;
m_MemCardScores [ i ] [ j ] . fSurviveTime = 0 ;
}
2002-12-17 05:07:56 +00:00
}
2002-07-02 17:34:20 +00:00
2003-02-05 19:16:00 +00:00
/*
* GetSongDir() contains a path to the song, possibly a full path, eg:
* Songs\Group\SongName or
* My Other Song Folder\Group\SongName or
* c:\Corny J-pop\Group\SongName
*
* Most course group names are "Group\SongName", so we want to
* match against the last two elements. Let's also support
* "SongName" alone, since the group is only important when it's
* potentially ambiguous.
*
* Let's *not* support "Songs\Group\SongName" in course files.
* That's probably a common error, but that would result in
* course files floating around that only work for people who put
* songs in "Songs"; we don't want that.
*/
Song * Course : : FindSong ( CString sSongDir )
{
const vector < Song * > & apSongs = SONGMAN - > GetAllSongs ( ) ;
CStringArray split_SongDir ;
sSongDir . Replace ( " \\ " , " / " ) ;
split ( sSongDir , " / " , split_SongDir , true ) ;
if ( split_SongDir . size ( ) > 2 )
{
LOG - > Warn ( " Course file \" %s \" path \" %s \" should contain "
" at most one backslash; ignored. " ,
m_sPath . GetString ( ) , sSongDir . GetString ( ) ) ;
return NULL ;
}
// foreach song
for ( unsigned i = 0 ; i < apSongs . size ( ) ; i + + )
{
CString dir = apSongs [ i ] - > GetSongDir ( ) ;
dir . Replace ( " \\ " , " / " ) ;
CStringArray splitted ; /* splat! */
split ( dir , " / " , splitted , true ) ;
bool matches = true ;
int split_no = splitted . size ( ) - 1 ;
int SongDir_no = split_SongDir . size ( ) - 1 ;
while ( split_no > = 0 & & SongDir_no > = 0 ) {
if ( stricmp ( splitted [ split_no - - ] , split_SongDir [ SongDir_no - - ] ) )
matches = false ;
}
if ( matches )
return apSongs [ i ] ;
}
LOG - > Trace ( " Course \" %s \" : couldn't match song \" %s \" " ,
m_sPath . GetString ( ) , sSongDir . GetString ( ) ) ;
return NULL ;
}
void Course : : LoadFromCRSFile ( CString sPath )
2002-07-02 17:34:20 +00:00
{
2003-01-21 05:14:59 +00:00
m_sPath = sPath ; // save path
2002-07-27 19:29:51 +00:00
MsdFile msd ;
if ( ! msd . ReadFile ( sPath ) )
2002-12-21 18:36:10 +00:00
RageException : : Throw ( " Error opening CRS file '%s'. " , sPath . GetString ( ) ) ;
2002-07-02 17:34:20 +00:00
2002-07-27 19:29:51 +00:00
CString sDir , sFName , sExt ;
splitrelpath ( sPath , sDir , sFName , sExt ) ;
2002-07-02 17:34:20 +00:00
2002-07-27 19:29:51 +00:00
CStringArray arrayPossibleBanners ;
GetDirListing ( " Courses \\ " + sFName + " .png " , arrayPossibleBanners , false , true ) ;
GetDirListing ( " Courses \\ " + sFName + " .jpg " , arrayPossibleBanners , false , true ) ;
GetDirListing ( " Courses \\ " + sFName + " .bmp " , arrayPossibleBanners , false , true ) ;
GetDirListing ( " Courses \\ " + sFName + " .gif " , arrayPossibleBanners , false , true ) ;
2002-10-31 02:11:52 +00:00
if ( ! arrayPossibleBanners . empty ( ) )
2002-07-27 19:29:51 +00:00
m_sBannerPath = arrayPossibleBanners [ 0 ] ;
2002-07-02 17:34:20 +00:00
2003-01-14 22:10:04 +00:00
for ( unsigned i = 0 ; i < msd . GetNumValues ( ) ; i + + )
2002-07-02 17:34:20 +00:00
{
2003-01-14 22:44:30 +00:00
CString sValueName = msd . GetParam ( i , 0 ) ;
const MsdFile : : value_t & sParams = msd . GetValue ( i ) ;
2002-07-02 17:34:20 +00:00
2002-07-27 19:29:51 +00:00
// handle the data
if ( 0 = = stricmp ( sValueName , " COURSE " ) )
m_sName = sParams [ 1 ] ;
2002-07-02 17:34:20 +00:00
2002-07-27 19:29:51 +00:00
else if ( 0 = = stricmp ( sValueName , " REPEAT " ) )
2002-07-02 17:34:20 +00:00
{
2003-01-14 22:44:30 +00:00
CString str = sParams [ 1 ] ;
str . MakeLower ( ) ;
if ( str . Find ( " yes " ) ! = - 1 )
2002-07-27 19:29:51 +00:00
m_bRepeat = true ;
2002-07-02 17:34:20 +00:00
}
2002-07-27 19:29:51 +00:00
else if ( 0 = = stricmp ( sValueName , " LIVES " ) )
m_iLives = atoi ( sParams [ 1 ] ) ;
2002-07-02 17:34:20 +00:00
2002-07-31 04:06:34 +00:00
else if ( 0 = = stricmp ( sValueName , " EXTRA " ) )
m_iExtra = atoi ( sParams [ 1 ] ) ;
2002-07-27 19:29:51 +00:00
else if ( 0 = = stricmp ( sValueName , " SONG " ) )
2002-07-02 17:34:20 +00:00
{
2002-10-12 19:30:15 +00:00
CString sSongDir = sParams [ 1 ] ;
2002-07-27 19:29:51 +00:00
CString sNotesDescription = sParams [ 2 ] ;
2002-08-01 13:42:56 +00:00
CString sModifiers = sParams [ 3 ] ;
2002-07-02 17:34:20 +00:00
2002-10-12 19:30:15 +00:00
if ( ! sSongDir . GetLength ( ) ) {
/* Err. */
2002-10-29 07:58:44 +00:00
LOG - > Trace ( " Course file '%s' has an empty #SONG. Ignored. " , sPath . GetString ( ) , sSongDir . GetString ( ) ) ;
2002-10-12 19:30:15 +00:00
continue ;
}
2003-02-05 19:16:00 +00:00
Song * pSong = FindSong ( sSongDir ) ;
2002-07-03 21:27:26 +00:00
2002-07-02 17:34:20 +00:00
if ( pSong = = NULL ) // we didn't find the Song
continue ; // skip this song
2002-08-01 13:42:56 +00:00
AddStage ( pSong , sNotesDescription , sModifiers ) ;
2002-07-02 17:34:20 +00:00
}
else
2002-10-29 07:58:44 +00:00
LOG - > Trace ( " Unexpected value named '%s' " , sValueName . GetString ( ) ) ;
2002-07-02 17:34:20 +00:00
}
2003-02-10 23:17:57 +00:00
static TitleSubst tsub ;
CString ignore ;
tsub . Subst ( m_sName , ignore , ignore ,
ignore , ignore , ignore ) ;
2002-07-02 17:34:20 +00:00
}
2002-07-23 01:41:40 +00:00
2003-01-03 05:56:28 +00:00
void Course : : CreateEndlessCourseFromGroupAndDifficulty ( CString sGroupName , Difficulty dc , vector < Song * > & apSongsInGroup )
2002-07-23 01:41:40 +00:00
{
2002-07-29 03:06:55 +00:00
m_bRepeat = true ;
m_bRandomize = true ;
m_iLives = - 1 ;
2002-08-01 20:30:40 +00:00
CStringArray asPossibleBannerPaths ;
GetDirListing ( " Songs \\ " + sGroupName + " \\ banner.png " , asPossibleBannerPaths , false , true ) ;
GetDirListing ( " Songs \\ " + sGroupName + " \\ banner.jpg " , asPossibleBannerPaths , false , true ) ;
GetDirListing ( " Songs \\ " + sGroupName + " \\ banner.gif " , asPossibleBannerPaths , false , true ) ;
GetDirListing ( " Songs \\ " + sGroupName + " \\ banner.bmp " , asPossibleBannerPaths , false , true ) ;
2002-10-31 02:11:52 +00:00
if ( ! asPossibleBannerPaths . empty ( ) )
2002-08-01 20:30:40 +00:00
m_sBannerPath = asPossibleBannerPaths [ 0 ] ;
2002-07-23 01:41:40 +00:00
CString sShortGroupName = SONGMAN - > ShortenGroupName ( sGroupName ) ;
m_sName = sShortGroupName + " " ;
switch ( dc )
{
2002-09-29 05:06:18 +00:00
case DIFFICULTY_EASY : m_sName + = " Easy " ; break ;
case DIFFICULTY_MEDIUM : m_sName + = " Medium " ; break ;
case DIFFICULTY_HARD : m_sName + = " Hard " ; break ;
2003-01-26 07:33:03 +00:00
default :
ASSERT ( 0 ) ;
2002-07-23 01:41:40 +00:00
}
2002-10-31 02:11:52 +00:00
for ( unsigned s = 0 ; s < apSongsInGroup . size ( ) ; s + + )
2002-07-23 01:41:40 +00:00
{
Song * pSong = apSongsInGroup [ s ] ;
2002-09-29 05:06:18 +00:00
AddStage ( pSong , DifficultyToString ( dc ) , " " ) ;
2002-07-23 01:41:40 +00:00
}
2002-09-04 03:29:42 +00:00
Shuffle ( ) ;
2002-07-23 01:41:40 +00:00
}
2002-09-04 03:29:42 +00:00
void Course : : Shuffle ( )
{
/* Shuffle the list. */
2002-12-17 05:29:50 +00:00
for ( int i = 0 ; i < GetNumStages ( ) ; + + i )
2002-12-17 05:41:04 +00:00
swap ( order [ i ] , order [ rand ( ) % GetNumStages ( ) ] ) ;
2002-09-04 03:29:42 +00:00
}
2002-07-23 01:41:40 +00:00
Notes * Course : : GetNotesForStage ( int iStage )
{
2002-12-17 05:41:04 +00:00
Song * pSong = GetSong ( iStage ) ;
CString sDescription = entries [ iStage ] . description ;
2002-10-31 02:11:52 +00:00
2003-02-05 20:00:49 +00:00
/* First, search for an exact description match. */
unsigned i ;
for ( i = 0 ; i < pSong - > m_apNotes . size ( ) ; i + + )
2002-07-23 01:41:40 +00:00
{
Notes * pNotes = pSong - > m_apNotes [ i ] ;
2003-01-21 22:23:01 +00:00
if ( ! GAMESTATE - > GetCurrentStyleDef ( ) - > MatchesNotesType ( pNotes - > m_NotesType ) )
continue ;
2002-07-23 01:41:40 +00:00
2003-02-05 20:00:49 +00:00
if ( ! pNotes - > GetDescription ( ) . CompareNoCase ( sDescription ) )
return pNotes ;
}
/* If that failed, try to do a difficulty match. */
Difficulty dc = StringToDifficulty ( sDescription ) ;
for ( i = 0 ; i < pSong - > m_apNotes . size ( ) ; i + + )
{
Notes * pNotes = pSong - > m_apNotes [ i ] ;
if ( ! GAMESTATE - > GetCurrentStyleDef ( ) - > MatchesNotesType ( pNotes - > m_NotesType ) )
continue ;
if ( dc = = pNotes - > GetDifficulty ( ) )
2002-07-23 01:41:40 +00:00
return pNotes ;
}
return NULL ;
}
2002-12-17 05:23:45 +00:00
Song * Course : : GetSong ( int iStage ) const
2002-12-17 05:07:56 +00:00
{
2002-12-17 05:41:04 +00:00
return entries [ iStage ] . song ;
2002-12-17 05:07:56 +00:00
}
2002-12-17 05:23:45 +00:00
CString Course : : GetDescription ( int iStage ) const
2002-12-17 05:07:56 +00:00
{
2002-12-17 05:41:04 +00:00
return entries [ iStage ] . description ;
2002-12-17 05:07:56 +00:00
}
void Course : : AddStage ( Song * pSong , CString sDescription , CString sModifiers )
{
2002-12-17 05:41:04 +00:00
course_entry e ;
e . song = pSong ;
e . description = sDescription ;
e . modifiers = sModifiers ;
entries . push_back ( e ) ;
2002-12-17 05:07:56 +00:00
2002-12-17 05:41:04 +00:00
order . push_back ( order . size ( ) ) ;
2002-12-17 05:07:56 +00:00
}
2002-07-23 01:41:40 +00:00
2002-09-04 03:29:42 +00:00
/* When bShuffled is true, returns courses in the song ordering list. */
void Course : : GetSongAndNotesForCurrentStyle (
2003-01-03 05:56:28 +00:00
vector < Song * > & apSongsOut ,
vector < Notes * > & apNotesOut ,
2002-09-04 03:29:42 +00:00
CStringArray & asModifiersOut ,
bool bShuffled )
2002-07-23 01:41:40 +00:00
{
2002-12-17 05:29:50 +00:00
for ( int i = 0 ; i < GetNumStages ( ) ; i + + )
2002-07-23 01:41:40 +00:00
{
2002-12-17 05:41:04 +00:00
int num = bShuffled ? order [ i ] : i ;
2002-09-04 03:29:42 +00:00
2002-12-17 05:41:04 +00:00
Song * pSong = GetSong ( num ) ;
2002-09-04 03:29:42 +00:00
Notes * pNotes = GetNotesForStage ( num ) ;
2002-12-17 05:41:04 +00:00
CString sModifiers = entries [ num ] . modifiers ;
2002-07-23 01:41:40 +00:00
2002-09-16 18:25:29 +00:00
if ( ! pSong - > HasMusic ( ) )
continue ; // skip
2002-07-23 01:41:40 +00:00
if ( pNotes = = NULL )
continue ; // skip
2002-10-31 04:23:39 +00:00
apSongsOut . push_back ( pSong ) ;
apNotesOut . push_back ( pNotes ) ;
asModifiersOut . push_back ( sModifiers ) ;
2002-07-23 01:41:40 +00:00
}
2002-07-27 19:29:51 +00:00
}
2002-10-28 05:30:45 +00:00
RageColor Course : : GetColor ( )
2002-07-27 19:29:51 +00:00
{
// This could be made smarter
2002-12-17 05:29:50 +00:00
if ( GetNumStages ( ) > = 7 )
2002-10-28 05:30:45 +00:00
return RageColor ( 1 , 0 , 0 , 1 ) ; // red
2002-12-17 05:29:50 +00:00
else if ( GetNumStages ( ) > = 4 )
2002-10-28 05:30:45 +00:00
return RageColor ( 1 , 0.5f , 0 , 1 ) ; // orange
2002-07-27 19:29:51 +00:00
else
2002-10-28 05:30:45 +00:00
return RageColor ( 0 , 1 , 0 , 1 ) ; // green
2002-07-27 19:29:51 +00:00
}
2003-01-21 05:14:59 +00:00
void Course : : GetPlayerOptions ( int iStage , PlayerOptions * pPO_out ) const
2002-07-28 20:28:37 +00:00
{
2003-01-21 05:14:59 +00:00
pPO_out - > FromString ( entries [ iStage ] . modifiers ) ;
2002-07-28 20:28:37 +00:00
}
2003-01-21 05:14:59 +00:00
void Course : : GetSongOptions ( SongOptions * pSO_out ) const
2002-07-28 20:28:37 +00:00
{
* pSO_out = SongOptions ( ) ;
2003-02-05 09:01:09 +00:00
//hack: The lifebar modifiers were not showing up (in the extra stages) - had to add it here.
if ( entries . size ( ) > 0 )
pSO_out - > FromString ( entries [ 0 ] . modifiers ) ;
2002-07-28 20:28:37 +00:00
pSO_out - > m_LifeType = ( m_iLives = = - 1 ) ? SongOptions : : LIFE_BAR : SongOptions : : LIFE_BATTERY ;
if ( m_iLives ! = - 1 )
pSO_out - > m_iBatteryLives = m_iLives ;
}
2002-12-17 05:23:45 +00:00
int Course : : GetNumStages ( ) const
{
2002-12-17 05:41:04 +00:00
return entries . size ( ) ;
2002-12-17 05:23:45 +00:00
}
2003-01-24 02:43:07 +00:00
2003-01-27 02:00:38 +00:00
struct CourseScoreToInsert
2003-01-24 02:43:07 +00:00
{
PlayerNumber pn ;
2003-01-27 02:00:38 +00:00
int iDancePoints ;
float fSurviveTime ;
2003-01-24 02:43:07 +00:00
2003-01-27 02:00:38 +00:00
static int CompareDescending ( const CourseScoreToInsert & hs1 , const CourseScoreToInsert & hs2 )
2003-01-24 02:43:07 +00:00
{
if ( hs1 . iDancePoints > hs2 . iDancePoints ) return - 1 ;
else if ( hs1 . iDancePoints = = hs2 . iDancePoints ) return 0 ;
else return + 1 ;
}
2003-01-27 02:00:38 +00:00
static void SortDescending ( vector < CourseScoreToInsert > & vHSout )
2003-01-24 02:43:07 +00:00
{
sort ( vHSout . begin ( ) , vHSout . end ( ) , CompareDescending ) ;
}
} ;
2003-01-27 02:00:38 +00:00
void Course : : AddMachineRecords ( NotesType nt , int iDancePoints [ NUM_PLAYERS ] , float fSurviveTime [ NUM_PLAYERS ] , int iRankingIndexOut [ NUM_PLAYERS ] ) // set iNewRecordIndex = -1 if not a new record
2003-01-24 02:43:07 +00:00
{
2003-01-27 02:00:38 +00:00
vector < CourseScoreToInsert > vHS ;
2003-01-24 02:43:07 +00:00
for ( int p = 0 ; p < NUM_PLAYERS ; p + + )
{
2003-01-27 02:00:38 +00:00
iRankingIndexOut [ p ] = - 1 ;
2003-01-24 02:43:07 +00:00
if ( ! GAMESTATE - > IsPlayerEnabled ( p ) )
continue ; // skip
2003-01-27 02:00:38 +00:00
CourseScoreToInsert hs ;
2003-01-24 02:43:07 +00:00
hs . iDancePoints = iDancePoints [ p ] ;
hs . fSurviveTime = fSurviveTime [ p ] ;
hs . pn = ( PlayerNumber ) p ;
vHS . push_back ( hs ) ;
}
// Sort descending before inserting.
// This guarantees that a high score will not switch poitions on us when we later insert scores for the other player
2003-01-27 02:00:38 +00:00
CourseScoreToInsert : : SortDescending ( vHS ) ;
2003-01-24 02:43:07 +00:00
for ( unsigned i = 0 ; i < vHS . size ( ) ; i + + )
{
2003-01-27 02:00:38 +00:00
CourseScoreToInsert & newHS = vHS [ i ] ;
MachineScore * machineScores = m_MachineScores [ nt ] ;
for ( int i = 0 ; i < NUM_RANKING_LINES ; i + + )
2003-01-24 02:43:07 +00:00
{
if ( newHS . iDancePoints > machineScores [ i ] . iDancePoints )
{
// We found the insert point. Shift down.
2003-01-27 02:00:38 +00:00
for ( int j = i + 1 ; j < NUM_RANKING_LINES ; j + + )
2003-01-24 02:43:07 +00:00
machineScores [ j ] = machineScores [ j - 1 ] ;
// insert
2003-01-27 02:00:38 +00:00
machineScores [ i ] . fSurviveTime = newHS . fSurviveTime ;
machineScores [ i ] . iDancePoints = newHS . iDancePoints ;
machineScores [ i ] . sName = " STEP " ;
iRankingIndexOut [ newHS . pn ] = i ;
2003-01-24 02:43:07 +00:00
}
}
}
}
2003-01-27 02:00:38 +00:00
bool Course : : AddMemCardRecord ( PlayerNumber pn , NotesType nt , int iDancePoints , float fSurviveTime ) // return true if new record
2003-01-24 02:43:07 +00:00
{
2003-01-27 02:00:38 +00:00
MemCardScore & hs = m_MemCardScores [ nt ] [ pn ] ;
2003-01-24 02:43:07 +00:00
if ( iDancePoints > hs . iDancePoints )
{
hs . iDancePoints = iDancePoints ;
hs . fSurviveTime = fSurviveTime ;
return true ;
}
return false ;
}
2002-07-27 19:29:51 +00:00
//
// Sorting stuff
//
2002-10-24 08:17:09 +00:00
static int CompareCoursePointersByDifficulty ( const Course * pCourse1 , const Course * pCourse2 )
2002-07-27 19:29:51 +00:00
{
2002-12-17 05:29:50 +00:00
return pCourse1 - > GetNumStages ( ) < pCourse2 - > GetNumStages ( ) ;
2002-07-27 19:29:51 +00:00
}
2003-01-03 05:56:28 +00:00
void SortCoursePointerArrayByDifficulty ( vector < Course * > & apCourses )
2002-07-27 19:29:51 +00:00
{
2002-10-24 08:40:27 +00:00
sort ( apCourses . begin ( ) , apCourses . end ( ) , CompareCoursePointersByDifficulty ) ;
2002-07-27 19:29:51 +00:00
}