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"
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
{
m_MachineScores [ i ][ j ]. iDancePoints = 0 ;
m_MachineScores [ i ][ j ]. fSurviveTime = 0 ;
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-01-03 05:56:28 +00:00
void Course :: LoadFromCRSFile ( CString sPath , vector < Song *> & apSongs )
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
// handle the data
2002-07-27 19:29:51 +00:00
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-02 17:34:20 +00:00
m_bRepeat = true ;
}
2002-07-27 19:29:51 +00:00
else if ( 0 == stricmp ( sValueName , "LIVES" ) )
m_iLives = atoi ( sParams [ 1 ] );
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-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 ;
}
2002-07-03 21:27:26 +00:00
2002-12-13 23:41:11 +00:00
/* GetSongDir() contains a path to the song, possibly a full path, eg:
2002-10-12 19:30:15 +00:00
* 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.
*/
CStringArray split_SongDir ;
2003-01-26 09:15:52 +00:00
sSongDir . Replace ( " \\ " , "/" );
split ( sSongDir , "/" , split_SongDir , true );
2002-10-12 19:30:15 +00:00
2002-10-31 02:11:52 +00:00
if ( split_SongDir . size () > 2 )
2002-07-27 19:29:51 +00:00
{
2002-10-12 19:30:15 +00:00
LOG -> Warn ( "Course file \" %s \" path \" %s \" should contain "
"at most one backslash; ignored." ,
2002-10-13 17:37:29 +00:00
( const char * ) sPath , ( const char * ) sSongDir );
2002-10-12 19:30:15 +00:00
continue ;
}
Song * pSong = NULL ;
// foreach song
2002-10-31 02:11:52 +00:00
for ( unsigned i = 0 ; pSong == NULL && i < apSongs . size (); i ++ )
2002-10-12 19:30:15 +00:00
{
2003-01-26 09:15:52 +00:00
CString dir = apSongs [ i ] -> GetSongDir ();
dir . Replace ( " \\ " , "/" );
2002-10-12 19:30:15 +00:00
CStringArray splitted ;
2003-01-26 09:15:52 +00:00
split ( dir , "/" , splitted , true );
2002-10-12 19:30:15 +00:00
bool matches = true ;
2002-10-31 02:11:52 +00:00
int split_no = splitted . size () - 1 ;
int SongDir_no = split_SongDir . size () - 1 ;
2002-10-12 19:30:15 +00:00
while ( split_no >= 0 && SongDir_no >= 0 ) {
2002-10-13 17:37:29 +00:00
if ( stricmp ( splitted [ split_no -- ], split_SongDir [ SongDir_no -- ] ) )
2002-10-12 19:30:15 +00:00
matches = false ;
}
if ( matches )
2002-07-02 17:34:20 +00:00
pSong = apSongs [ i ];
2002-07-27 19:29:51 +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
}
}
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-01-21 22:23:01 +00:00
for ( unsigned 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 ;
2003-02-04 21:11:20 +00:00
if ( ! pNotes -> GetDescription (). CompareNoCase ( sDescription ) ||
! DifficultyToString ( pNotes -> GetDifficulty ()). CompareNoCase ( sDescription ) )
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
}