示例#1
0
unsigned Steps::GetHash() const
{
	if( parent )
		return parent->GetHash();
	if( m_iHash )
		return m_iHash;
	if( m_sNoteDataCompressed.empty() )
	{
		if( !m_bNoteDataIsFilled )
			return 0; // No data, no hash.
		NoteDataUtil::GetSMNoteDataString( *m_pNoteData, m_sNoteDataCompressed );
	}
	m_iHash = GetHashForString( m_sNoteDataCompressed );
	return m_iHash;
}
示例#2
0
	virtual void PopulateFileSet( FileSet &fs, const RString &sPath )
	{
		if( sPath != "." )
			return;
		if( g_TestFile == "" )
			return;

		fs.files.clear();

		File f;
		f.SetName( g_TestFilename );
		f.dir = false;
		f.size = g_TestFile.size();
		f.hash = GetHashForString( g_TestFile );
		fs.files.insert(f);
	}
unsigned int GetHashForDirectory( const CString &sDir )
{
	unsigned int hash = 0;

	hash += GetHashForString( sDir );

	CStringArray arrayFiles;
	GetDirListing( sDir+"*", arrayFiles, false );
	for( unsigned i=0; i<arrayFiles.size(); i++ )
	{
		const CString sFilePath = sDir + arrayFiles[i];
		hash += GetHashForFile( sFilePath );
	}

	return hash; 
}
示例#4
0
void BackgroundImpl::LoadFromSong( const Song* pSong )
{
	Init();
	Unload();
	m_pSong = pSong;
	m_StaticBackgroundDef.m_sFile1 = SONG_BACKGROUND_FILE;

	if( g_fBGBrightness == 0.0f )
		return;

	// Choose a bunch of backgrounds that we'll use for the random file marker
	{
		vector<RString> vsThrowAway, vsNames;
		switch( g_RandomBackgroundMode )
		{
		default:
			ASSERT_M( 0, ssprintf("Invalid RandomBackgroundMode: %i", int(g_RandomBackgroundMode)) );
			break;
		case BGMODE_OFF:
			break;
		case BGMODE_ANIMATIONS:
			BackgroundUtil::GetGlobalBGAnimations( pSong, "", vsThrowAway, vsNames );
			break;
		case BGMODE_RANDOMMOVIES:
			BackgroundUtil::GetGlobalRandomMovies( pSong, "", vsThrowAway, vsNames, true, true );
			break;
		}

		// Pick the same random items every time the song is played.
		RandomGen rnd( GetHashForString(pSong->GetSongDir()) );
		random_shuffle( vsNames.begin(), vsNames.end(), rnd );
		int iSize = min( (int)g_iNumBackgrounds, (int)vsNames.size() );
		vsNames.resize( iSize );

		FOREACH_CONST( RString, vsNames, s )
		{
			BackgroundDef bd;
			bd.m_sFile1 = *s;
			m_RandomBGAnimations.push_back( bd );
		}
	}
示例#5
0
bool Course::GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail ) const
{
	trail.Init();

	// XXX: Why are beginner and challenge excluded here? -Wolfman2000
	// No idea, probably an obsolete design decision from ITG, removing
	// exclusion here, but there's some other area that prevents it too. -Kyz
	/*
	switch( cd )
	{
		case Difficulty_Beginner:
			return false;
		case Difficulty_Challenge:
			return false;
		default: break;
	}
	*/

	// 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_iStageSeed + GetHashForString(m_sMainTitle) );

	vector<CourseEntry> tmp_entries;
	if( m_bShuffle )
	{
		/* Always randomize the same way per round.  Otherwise, the displayed course
		* will change every time it's viewed, and the displayed order will have no
		* bearing on what you'll actually play. */
		tmp_entries = m_vEntries;
		random_shuffle( tmp_entries.begin(), tmp_entries.end(), rnd );
	}

	const vector<CourseEntry> &entries = m_bShuffle ? tmp_entries:m_vEntries;

	// This can take some time, so don't fill it out unless we need it.
	vector<Song*> vSongsByMostPlayed;
	vector<Song*> AllSongsShuffled;

	trail.m_StepsType = st;
	trail.m_CourseType = GetCourseType();
	trail.m_CourseDifficulty = cd;
	trail.m_vEntries.reserve(entries.size());

	// Set to true if CourseDifficulty is able to change something.
	bool bCourseDifficultyIsSignificant = (cd == Difficulty_Medium);

	

	// Resolve each entry to a Song and Steps.
	if( trail.m_CourseType == COURSE_TYPE_ENDLESS )
	{
		GetTrailUnsortedEndless(entries, trail, st, cd, rnd, bCourseDifficultyIsSignificant);
	}
	else
	{
		vector<SongAndSteps> vSongAndSteps;
		FOREACH_CONST( CourseEntry, entries, e )
		{
			SongAndSteps resolved;	// fill this in
			SongCriteria soc = e->songCriteria;

			Song *pSong = e->songID.ToSong();
			if( pSong )
			{
				soc.m_bUseSongAllowedList = true;
				soc.m_vpSongAllowedList.push_back( pSong );
			}
			soc.m_Tutorial = SongCriteria::Tutorial_No;
			soc.m_Locked = SongCriteria::Locked_Unlocked;
			if( !soc.m_bUseSongAllowedList )
				soc.m_iMaxStagesForSong = 1;

			StepsCriteria stc = e->stepsCriteria;
			stc.m_st = st;
			stc.m_Locked = StepsCriteria::Locked_Unlocked;

			const bool bSameSongCriteria = e != entries.begin() && ( e - 1 )->songCriteria == soc;
			const bool bSameStepsCriteria = e != entries.begin() && ( e - 1 )->stepsCriteria == stc;

			if( pSong )
			{
				StepsUtil::GetAllMatching( pSong, stc, vSongAndSteps );
			}
			else if( vSongAndSteps.empty() || !( bSameSongCriteria && bSameStepsCriteria ) )
			{
				vSongAndSteps.clear();
				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 )
			{
				const TrailEntry &teLast = trail.m_vEntries.back();
				RemoveIf( vSongAndSteps, SongIsEqual( teLast.pSong ) );
			}

			// if there are no songs to choose from, abort this CourseEntry
			if( vSongAndSteps.empty() )
				continue;

			vector<Song*> vpSongs;
			typedef vector<Steps*> StepsVector;
			map<Song*, StepsVector> mapSongToSteps;
			FOREACH_CONST( SongAndSteps, vSongAndSteps, sas )
			{
				StepsVector &v = mapSongToSteps[ sas->pSong ];

				v.push_back( sas->pSteps );
				if( v.size() == 1 )
					vpSongs.push_back( sas->pSong );
			}

			CourseSortSongs( e->songSort, vpSongs, rnd );

			ASSERT( e->iChooseIndex >= 0 );
			if( e->iChooseIndex < int( vSongAndSteps.size() ) )
			{
				resolved.pSong = vpSongs[ e->iChooseIndex ];
				const vector<Steps*> &mappedSongs = mapSongToSteps[ resolved.pSong ];
				resolved.pSteps = mappedSongs[ RandomInt( mappedSongs.size() ) ];
			}
			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 = resolved.pSteps->GetDifficulty();
			int iLowMeter = e->stepsCriteria.m_iLowMeter;
			int iHighMeter = e->stepsCriteria.m_iHighMeter;
			if( cd != Difficulty_Medium  &&  !e->bNoDifficult )
			{
				Difficulty new_dc = ( Difficulty )( dc + cd - Difficulty_Medium );
				new_dc = clamp( new_dc, ( Difficulty )0, ( Difficulty )( Difficulty_Edit - 1 ) );
				/*
				// re-edit this code to work using the metric.
				Difficulty new_dc;
				if( INCLUDE_BEGINNER_STEPS )
				{
				// don't factor in the course difficulty if we're including
				// beginner steps -aj
				new_dc = clamp( dc, Difficulty_Beginner, (Difficulty)(Difficulty_Edit-1) );
				}
				else
				{
				new_dc = (Difficulty)(dc + cd - Difficulty_Medium);
				new_dc = clamp( new_dc, (Difficulty)0, (Difficulty)(Difficulty_Edit-1) );
				}
				*/

				bool bChangedDifficulty = false;
				if( new_dc != dc )
				{
					Steps* pNewSteps = SongUtil::GetStepsByDifficulty( resolved.pSong, st, new_dc );
					if( pNewSteps )
					{
						dc = new_dc;
						resolved.pSteps = pNewSteps;
						bChangedDifficulty = true;
						bCourseDifficultyIsSignificant = true;
					}
				}

				/* Hack: We used to adjust low_meter/high_meter above while searching for
				* songs.  However, that results in a different song being chosen for
				* difficult courses, which is bad when LockCourseDifficulties is disabled;
				* each player can end up with a different song.  Instead, choose based
				* on the original range, bump the steps based on course difficulty, and
				* then retroactively tweak the low_meter/high_meter so course displays
				* line up. */
				if( e->stepsCriteria.m_difficulty == Difficulty_Invalid && bChangedDifficulty )
				{
					/* Minimum and maximum to add to make the meter range contain the actual
					* meter: */
					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. */
					iMinDist = min( max( iMinDist, -iLowMeter + 1 ), iMaxDist );
					iMaxDist = max( min( iMaxDist, MAX_BOTTOM_RANGE - iHighMeter ), iMinDist );

					int iAdd;
					if( iMaxDist == iMinDist )
						iAdd = iMaxDist;
					else
						iAdd = rnd( iMaxDist - iMinDist ) + iMinDist;
					iLowMeter += iAdd;
					iHighMeter += iAdd;
				}
			}

			TrailEntry te;
			te.pSong = resolved.pSong;
			te.pSteps = resolved.pSteps;
			te.Modifiers = e->sModifiers;
			te.Attacks = e->attacks;
			te.bSecret = e->bSecret;
			te.iLowMeter = iLowMeter;
			te.iHighMeter = iHighMeter;

			/* If we chose based on meter (not difficulty), then store Difficulty_Invalid, so
			* other classes can tell that we used meter. */
			if( e->stepsCriteria.m_difficulty == Difficulty_Invalid )
			{
				te.dc = Difficulty_Invalid;
			}
			else
			{
				/* Otherwise, store the actual difficulty we got (post-course-difficulty).
				* This may or may not be the same as e.difficulty. */
				te.dc = dc;
			}
			trail.m_vEntries.push_back( te );

			// LOG->Trace( "Chose: %s, %d", te.pSong->GetSongDir().c_str(), te.pSteps->GetMeter() );

			if( IsAnEdit() && MAX_SONGS_IN_EDIT_COURSE > 0 &&
				int( trail.m_vEntries.size() ) >= MAX_SONGS_IN_EDIT_COURSE )
			{
				break;
			}
		}
示例#6
0
bool Course::GetTrailUnsorted( StepsType st, CourseDifficulty cd, Trail &trail ) const
{
	trail.Init();

	// XXX: Why are beginner and challenge excluded here? -Wolfman2000
	switch( cd )
	{
		case Difficulty_Beginner:
			return false;
		case Difficulty_Challenge:
			return false;
		default: break;
	}

	// 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_iStageSeed + GetHashForString(m_sMainTitle) );

	vector<CourseEntry> tmp_entries;
	if( m_bShuffle )
	{
		/* Always randomize the same way per round.  Otherwise, the displayed course
		* will change every time it's viewed, and the displayed order will have no
		* bearing on what you'll actually play. */
		tmp_entries = m_vEntries;
		random_shuffle( tmp_entries.begin(), tmp_entries.end(), rnd );
	}

	const vector<CourseEntry> &entries = m_bShuffle ? tmp_entries:m_vEntries;

	// This can take some time, so don't fill it out unless we need it.
	vector<Song*> vSongsByMostPlayed;
	vector<Song*> AllSongsShuffled;

	trail.m_StepsType = st;
	trail.m_CourseType = GetCourseType();
	trail.m_CourseDifficulty = cd;

	// Set to true if CourseDifficulty is able to change something.
	bool bCourseDifficultyIsSignificant = (cd == Difficulty_Medium);

	vector<Song*> vpAllPossibleSongs;
	vector<SongAndSteps> vSongAndSteps;

	// Resolve each entry to a Song and Steps.
	FOREACH_CONST( CourseEntry, entries, e )
	{
		SongAndSteps resolved;	// fill this in
		SongCriteria soc = e->songCriteria;

		Song *pSong = e->songID.ToSong();
		if( pSong )
		{
			soc.m_bUseSongAllowedList = true;
			soc.m_vpSongAllowedList.push_back( pSong );
		}
		soc.m_Tutorial = SongCriteria::Tutorial_No;
		soc.m_Locked = SongCriteria::Locked_Unlocked;
		if( !soc.m_bUseSongAllowedList )
			soc.m_iMaxStagesForSong = 1;

		StepsCriteria stc = e->stepsCriteria;
		stc.m_st = st;
		stc.m_Locked = StepsCriteria::Locked_Unlocked;

		const bool bSameSongCriteria  = e != entries.begin() && (e-1)->songCriteria == soc;
		const bool bSameStepsCriteria = e != entries.begin() && (e-1)->stepsCriteria == stc;

		if( pSong )
		{
			StepsUtil::GetAllMatching( pSong, stc, vSongAndSteps );
		}
		else if( vSongAndSteps.empty() || !(bSameSongCriteria && bSameStepsCriteria) )
		{
			vSongAndSteps.clear();
			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 )
		{
			const TrailEntry &teLast = trail.m_vEntries.back();
			RemoveIf( vSongAndSteps, SongIsEqual(teLast.pSong) );
		}

		// if there are no songs to choose from, abort this CourseEntry
		if( vSongAndSteps.empty() )
			continue;

		vector<Song*> vpSongs;
		typedef vector<Steps*> StepsVector;
		map<Song*,StepsVector> mapSongToSteps;
		FOREACH_CONST( SongAndSteps, vSongAndSteps, sas )
		{
			StepsVector &v = mapSongToSteps[sas->pSong];

			v.push_back( sas->pSteps );
			if( v.size() == 1 )
				vpSongs.push_back( sas->pSong );
		}
示例#7
0
CString BannerCache::GetBannerCachePath( CString BannerPath )
{
	/* Use GetHashForString, not ForFile, since we don't want to spend time
	 * checking the file size and date. */
	return ssprintf( CACHE_DIR "Banners/%u", GetHashForString(BannerPath) );
}
unsigned int GetHashForFile( const CString &sPath )
{
	return GetHashForString( sPath ) + FILEMAN->GetFileHash( sPath );
}
示例#9
0
CString SongCacheIndex::GetCacheFilePath( const CString &sGroup, const CString &sPath )
{
	/* Use GetHashForString, not ForFile, since we don't want to spend time
	 * checking the file size and date. */
	return ssprintf( "%s/%s/%u", CACHE_DIR, sGroup.c_str(), GetHashForString(sPath) );
}