#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" #include "PrefsManager.h" #include "Song.h" #include "GameManager.h" #include "SongManager.h" #include "GameState.h" #include "RageException.h" #include "RageLog.h" #include "MsdFile.h" #include "PlayerOptions.h" #include "SongOptions.h" void Course::LoadFromCRSFile( CString sPath, CArray &apSongs ) { MsdFile msd; if( !msd.ReadFile(sPath) ) throw RageException( "Error opening CRS file '%s'.", sPath ); CString sDir, sFName, sExt; splitrelpath( sPath, sDir, sFName, sExt ); 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 ); if( arrayPossibleBanners.GetSize() > 0 ) m_sBannerPath = arrayPossibleBanners[0]; for( int i=0; iTrace( "Course file \"%s\" has an empty #SONG. Ignored.", (const char *) sPath, (const char *) sSongDir); continue; } /* m_sSongDir 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. */ CStringArray split_SongDir; split( sSongDir, "\\", split_SongDir, true ); if( split_SongDir.GetSize() > 2 ) { LOG->Warn( "Course file \"%s\" path \"%s\" should contain " "at most one backslash; ignored.", (const char *) sPath, (const char *) sSongDir); continue; } Song *pSong = NULL; // foreach song for( int i = 0; pSong == NULL && i < apSongs.GetSize(); i++ ) { CStringArray splitted; split( apSongs[i]->m_sSongDir, "\\", splitted, true ); bool matches = true; int split_no = splitted.GetSize()-1; int SongDir_no = split_SongDir.GetSize()-1; while( split_no >= 0 && SongDir_no >= 0 ) { if( stricmp(splitted[split_no--], split_SongDir[SongDir_no--] ) ) matches=false; } if(matches) pSong = apSongs[i]; } if( pSong == NULL ) // we didn't find the Song continue; // skip this song AddStage( pSong, sNotesDescription, sModifiers ); } else LOG->Trace( "Unexpected value named '%s'", sValueName ); } } void Course::CreateEndlessCourseFromGroupAndDifficulty( CString sGroupName, Difficulty dc, CArray &apSongsInGroup ) { m_bRepeat = true; m_bRandomize = true; m_iLives = -1; 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 ); if( asPossibleBannerPaths.GetSize() > 0 ) m_sBannerPath = asPossibleBannerPaths[0]; CString sShortGroupName = SONGMAN->ShortenGroupName( sGroupName ); m_sName = sShortGroupName + " "; switch( dc ) { case DIFFICULTY_EASY: m_sName += "Easy"; break; case DIFFICULTY_MEDIUM: m_sName += "Medium"; break; case DIFFICULTY_HARD: m_sName += "Hard"; break; } for( int s=0; sm_apNotes.GetSize(); i++ ) { Notes* pNotes = pSong->m_apNotes[i]; if( 0==stricmp(pNotes->m_sDescription, sDescription) && GAMESTATE->GetCurrentStyleDef()->MatchesNotesType(pNotes->m_NotesType) ) return pNotes; } // Didn't find a matching description. Try to match the Difficulty instead. Difficulty dc = Notes::DifficultyFromDescriptionAndMeter( sDescription, 5 ); for( i=0; im_apNotes.GetSize(); i++ ) { Notes* pNotes = pSong->m_apNotes[i]; if( pNotes->m_Difficulty == dc && GAMESTATE->GetCurrentStyleDef()->MatchesNotesType(pNotes->m_NotesType) ) return pNotes; } return NULL; } /* When bShuffled is true, returns courses in the song ordering list. */ void Course::GetSongAndNotesForCurrentStyle( CArray& apSongsOut, CArray& apNotesOut, CStringArray& asModifiersOut, bool bShuffled ) { for( int i=0; iHasMusic() ) continue; // skip if( pNotes == NULL ) continue; // skip apSongsOut.Add( pSong ); apNotesOut.Add( pNotes ); asModifiersOut.Add( sModifiers ); } } D3DXCOLOR Course::GetColor() { // This could be made smarter if( m_iStages >= 7 ) return D3DXCOLOR(1,0,0,1); // red else if( m_iStages >= 4 ) return D3DXCOLOR(1,0.5f,0,1); // orange else return D3DXCOLOR(0,1,0,1); // green } void Course::GetPlayerOptions( PlayerOptions* pPO_out ) { *pPO_out = PlayerOptions(); } void Course::GetSongOptions( SongOptions* pSO_out ) { *pSO_out = SongOptions(); pSO_out->m_LifeType = (m_iLives==-1) ? SongOptions::LIFE_BAR : SongOptions::LIFE_BATTERY; if( m_iLives != -1 ) pSO_out->m_iBatteryLives = m_iLives; } // // Sorting stuff // int CompareCoursePointersByDifficulty(const void *arg1, const void *arg2) { Course* pCourse1 = *(Course**)arg1; Course* pCourse2 = *(Course**)arg2; float fScore1 = (float)pCourse1->m_iStages; float fScore2 = (float)pCourse2->m_iStages; if( fScore1 < fScore2 ) return -1; else if( fScore1 == fScore2 ) return 0; else return 1; } void SortCoursePointerArrayByDifficulty( CArray &apCourses ) { qsort( apCourses.GetData(), apCourses.GetSize(), sizeof(Course*), CompareCoursePointersByDifficulty ); }