Example #1
0
bool StepsCriteria::Matches( const Song *pSong, const Steps *pSteps ) const
{
	if( m_difficulty != Difficulty_Invalid  &&  pSteps->GetDifficulty() != m_difficulty )
		return false;
	if( m_iLowMeter != -1  &&  pSteps->GetMeter() < m_iLowMeter )
		return false;
	if( m_iHighMeter != -1  &&  pSteps->GetMeter() > m_iHighMeter )
		return false;
	if( m_st != StepsType_Invalid  &&  pSteps->m_StepsType != m_st )
		return false;
	switch( m_Locked )
	{
	DEFAULT_FAIL(m_Locked);
	case Locked_Locked:
		if( UNLOCKMAN  &&  !UNLOCKMAN->StepsIsLocked(pSong,pSteps) )
			return false;
		break;
	case Locked_Unlocked:
		if( UNLOCKMAN  &&  UNLOCKMAN->StepsIsLocked(pSong,pSteps) )
			return false;
		break;
	case Locked_DontCare:
		break;
	}

	return true;
}
Example #2
0
// TODO: Move Course initialization after PROFILEMAN is created
static void CourseSortSongs( SongSort sort, vector<Song*> &vpPossibleSongs, RandomGen &rnd )
{
	switch( sort )
	{
	DEFAULT_FAIL(sort);
	case SongSort_Randomize:
		random_shuffle( vpPossibleSongs.begin(), vpPossibleSongs.end(), rnd );
		break;
	case SongSort_MostPlays:
		if( PROFILEMAN )
			SongUtil::SortSongPointerArrayByNumPlays( vpPossibleSongs, PROFILEMAN->GetMachineProfile(), true );	// descending
		break;
	case SongSort_FewestPlays:
		if( PROFILEMAN )
			SongUtil::SortSongPointerArrayByNumPlays( vpPossibleSongs, PROFILEMAN->GetMachineProfile(), false );	// ascending
		break;
	case SongSort_TopGrades:
		if( PROFILEMAN )
			SongUtil::SortSongPointerArrayByGrades( vpPossibleSongs, true );	// descending
		break;
	case SongSort_LowestGrades:
		if( PROFILEMAN )
			SongUtil::SortSongPointerArrayByGrades( vpPossibleSongs, false );	// ascending
		break;
	}
}
Example #3
0
int ScoreKeeperNormal::TapNoteScoreToGradePoints( TapNoteScore tns, bool bBeginner )
{
	if( !GAMESTATE->ShowW1() && tns == TNS_W1 )
		tns = TNS_W2;

	/* This is used for Oni percentage displays. Grading values are currently in
	 * StageStats::GetGrade. */
	int iWeight = 0;
	switch( tns )
	{
	DEFAULT_FAIL( tns );
	case TNS_None:		iWeight = 0;												break;
	case TNS_AvoidMine:	iWeight = 0;												break;
	case TNS_HitMine:	iWeight = g_iGradeWeight.GetValue(SE_HitMine);				break;
	case TNS_Miss:		iWeight = g_iGradeWeight.GetValue(SE_Miss);					break;
	case TNS_W5:		iWeight = g_iGradeWeight.GetValue(SE_W5);					break;
	case TNS_W4:		iWeight = g_iGradeWeight.GetValue(SE_W4);					break;
	case TNS_W3:		iWeight = g_iGradeWeight.GetValue(SE_W3);					break;
	case TNS_W2:		iWeight = g_iGradeWeight.GetValue(SE_W2);					break;
	case TNS_W1:		iWeight = g_iGradeWeight.GetValue(SE_W1);					break;
	case TNS_CheckpointHit:	iWeight = g_iGradeWeight.GetValue(SE_CheckpointHit);	break;
	case TNS_CheckpointMiss:iWeight = g_iGradeWeight.GetValue(SE_CheckpointMiss);	break;
	}
	if( bBeginner && PREFSMAN->m_bMercifulBeginner )
		iWeight = max( 0, iWeight );
	return iWeight;
}
bool RageDisplay::SaveScreenshot( RString sPath, GraphicsFileFormat format )
{
	RageTimer timer;
	RageSurface *surface = this->CreateScreenshot();
//	LOG->Trace( "CreateScreenshot took %f seconds", timer.GetDeltaTime() );
	/* Unless we're in lossless, resize the image to 640x480.  If we're saving lossy,
	 * there's no sense in saving 1280x960 screenshots, and we don't want to output
	 * screenshots in a strange (non-1) sample aspect ratio. */
	if( format != SAVE_LOSSLESS && format != SAVE_LOSSLESS_SENSIBLE )
	{
		// Maintain the DAR.
		ASSERT( GetActualVideoModeParams().fDisplayAspectRatio > 0 );
		int iHeight = 480;
		// This used to be lrintf. However, lrintf causes odd resolutions like
		// 639x480 (4:3) and 853x480 (16:9). ceilf gives correct values. -aj
		int iWidth = static_cast<int>(ceilf( iHeight * GetActualVideoModeParams().fDisplayAspectRatio ));
		timer.Touch();
		RageSurfaceUtils::Zoom( surface, iWidth, iHeight );
//		LOG->Trace( "%ix%i -> %ix%i (%.3f) in %f seconds", surface->w, surface->h, iWidth, iHeight, GetActualVideoModeParams().fDisplayAspectRatio, timer.GetDeltaTime() );
	}

	RageFile out;
	if( !out.Open( sPath, RageFile::WRITE ) )
	{
		LOG->Trace("Couldn't write %s: %s", sPath.c_str(), out.GetError().c_str() );
		SAFE_DELETE( surface );
		return false;
	}

	bool bSuccess = false;
	timer.Touch();
	RString strError = "";
	switch( format )
	{
	case SAVE_LOSSLESS:
		bSuccess = RageSurfaceUtils::SaveBMP( surface, out );
		break;
	case SAVE_LOSSLESS_SENSIBLE:
		bSuccess = RageSurfaceUtils::SavePNG( surface, out, strError );
		break;
	case SAVE_LOSSY_LOW_QUAL:
		bSuccess = RageSurfaceUtils::SaveJPEG( surface, out, false );
		break;
	case SAVE_LOSSY_HIGH_QUAL:
		bSuccess = RageSurfaceUtils::SaveJPEG( surface, out, true );
		break;
	DEFAULT_FAIL( format );
	}
//	LOG->Trace( "Saving Screenshot file took %f seconds.", timer.GetDeltaTime() );

	SAFE_DELETE( surface );

	if( !bSuccess )
	{
		LOG->Trace("Couldn't write %s: %s", sPath.c_str(), out.GetError().c_str() );
		return false;
	}

	return true;
}
void LifeMeterBar::ChangeLife( TapNoteScore score )
{
	float fDeltaLife=0.f;
	switch( score )
	{
	DEFAULT_FAIL( score );
	case TNS_W1:		fDeltaLife = m_fLifePercentChange.GetValue(SE_W1);	break;
	case TNS_W2:		fDeltaLife = m_fLifePercentChange.GetValue(SE_W2);	break;
	case TNS_W3:		fDeltaLife = m_fLifePercentChange.GetValue(SE_W3);	break;
	case TNS_W4:		fDeltaLife = m_fLifePercentChange.GetValue(SE_W4);	break;
	case TNS_W5:		fDeltaLife = m_fLifePercentChange.GetValue(SE_W5);	break;
	case TNS_Miss:		fDeltaLife = m_fLifePercentChange.GetValue(SE_Miss);	break;
	case TNS_HitMine:	fDeltaLife = m_fLifePercentChange.GetValue(SE_HitMine);	break;
	case TNS_None:		fDeltaLife = m_fLifePercentChange.GetValue(SE_Miss);	break;
	case TNS_CheckpointHit:	fDeltaLife = m_fLifePercentChange.GetValue(SE_CheckpointHit);	break;
	case TNS_CheckpointMiss:fDeltaLife = m_fLifePercentChange.GetValue(SE_CheckpointMiss);	break;
	}

	// this was previously if( IsHot()  &&  score < TNS_GOOD ) in 3.9... -freem
	if( IsHot()  &&  fDeltaLife < 0 )
		fDeltaLife = min( fDeltaLife, -0.10f );		// make it take a while to get back to "hot"

	switch( GAMESTATE->m_SongOptions.GetSong().m_DrainType )
	{
	DEFAULT_FAIL( GAMESTATE->m_SongOptions.GetSong().m_DrainType );
	case SongOptions::DRAIN_NORMAL:
		break;
	case SongOptions::DRAIN_NO_RECOVER:
		fDeltaLife = min( fDeltaLife, 0 );
		break;
	case SongOptions::DRAIN_SUDDEN_DEATH:
		if( score < MIN_STAY_ALIVE )
			fDeltaLife = -1.0f;
		else
			fDeltaLife = 0;
		break;
	}

	ChangeLife( fDeltaLife );
}
Example #6
0
void LifeMeterBar::ChangeLife( TapNoteScore score )
{
	float fDeltaLife=0.f;
	switch( score )
	{
	DEFAULT_FAIL( score );
	case TNS_W1:		fDeltaLife = m_fLifePercentChange.GetValue(SE_W1);	break;
	case TNS_W2:		fDeltaLife = m_fLifePercentChange.GetValue(SE_W2);	break;
	case TNS_W3:		fDeltaLife = m_fLifePercentChange.GetValue(SE_W3);	break;
	case TNS_W4:		fDeltaLife = m_fLifePercentChange.GetValue(SE_W4);	break;
	case TNS_W5:		fDeltaLife = m_fLifePercentChange.GetValue(SE_W5);	break;
	case TNS_Miss:		fDeltaLife = m_fLifePercentChange.GetValue(SE_Miss);	break;
	case TNS_HitMine:	fDeltaLife = m_fLifePercentChange.GetValue(SE_HitMine);	break;
	case TNS_None:		fDeltaLife = m_fLifePercentChange.GetValue(SE_Miss);	break;
	case TNS_CheckpointHit:	fDeltaLife = m_fLifePercentChange.GetValue(SE_CheckpointHit);	break;
	case TNS_CheckpointMiss:fDeltaLife = m_fLifePercentChange.GetValue(SE_CheckpointMiss);	break;
	}

	// this was previously if( IsHot()  &&  score < TNS_GOOD ) in 3.9... -freem
	if(PREFSMAN->m_HarshHotLifePenalty && IsHot()  &&  fDeltaLife < 0)
		fDeltaLife = min( fDeltaLife, -0.10f );		// make it take a while to get back to "hot"

	switch(m_pPlayerState->m_PlayerOptions.GetSong().m_DrainType)
	{
	DEFAULT_FAIL(m_pPlayerState->m_PlayerOptions.GetSong().m_DrainType);
	case DrainType_Normal:
		break;
	case DrainType_NoRecover:
		fDeltaLife = min( fDeltaLife, 0 );
		break;
	case DrainType_SuddenDeath:
		if( score < MIN_STAY_ALIVE )
			fDeltaLife = -1.0f;
		else
			fDeltaLife = 0;
		break;
	}

	ChangeLife( fDeltaLife );
}
Example #7
0
int ScoreKeeperNormal::HoldNoteScoreToGradePoints( HoldNoteScore hns, bool bBeginner )
{
	int iWeight = 0;
	switch( hns )
	{
	DEFAULT_FAIL( hns );
	case HNS_None:	iWeight = 0;									break;
	case HNS_LetGo:	iWeight = g_iGradeWeight.GetValue(SE_LetGo);	break;
	case HNS_Held:	iWeight = g_iGradeWeight.GetValue(SE_Held);		break;
	case HNS_Missed:	iWeight = g_iGradeWeight.GetValue(SE_Missed);		break;
	}
	if( bBeginner && PREFSMAN->m_bMercifulBeginner )
		iWeight = max( 0, iWeight );
	return iWeight;
}
Example #8
0
static void SuperMeterPercentChangeInit( size_t /*ScoreEvent*/ i, RString &sNameOut, float &defaultValueOut )
{
	ScoreEvent ci = (ScoreEvent)i;
	sNameOut = "SuperMeterPercentChange" + ScoreEventToString( ci );
	switch(ci)
	{
	case SE_CheckpointHit:	defaultValueOut = +0.05f; break;
	case SE_W1:				defaultValueOut = +0.05f; break;
	case SE_W2:				defaultValueOut = +0.04f; break;
	case SE_W3:				defaultValueOut = +0.02f; break;
	case SE_W4:				defaultValueOut = +0.00f; break;
	case SE_W5:				defaultValueOut = +0.00f; break;
	case SE_Miss:			defaultValueOut = -0.20f; break;
	case SE_HitMine:		defaultValueOut = -0.40f; break;
	case SE_CheckpointMiss:	defaultValueOut = -0.20f; break;
	case SE_Held:			defaultValueOut = +0.04f; break;
	case SE_LetGo:			defaultValueOut = -0.20f; break;
	DEFAULT_FAIL(ci);
	}
}
Example #9
0
void ScoreKeeperRave::HandleTapRowScore( const NoteData &nd, int iRow )
{
	TapNoteScore scoreOfLastTap;
	int iNumTapsInRow;
	float fPercentToMove = 0.0f;

	GetScoreOfLastTapInRow( nd, iRow, scoreOfLastTap, iNumTapsInRow );
	if( iNumTapsInRow <= 0 )
		return;
	switch( scoreOfLastTap )
	{
		DEFAULT_FAIL( scoreOfLastTap );
		case TNS_W1:	fPercentToMove = g_fSuperMeterPercentChange[SE_W1];		break;
		case TNS_W2:	fPercentToMove = g_fSuperMeterPercentChange[SE_W2];		break;
		case TNS_W3:	fPercentToMove = g_fSuperMeterPercentChange[SE_W3];		break;
		case TNS_W4:	fPercentToMove = g_fSuperMeterPercentChange[SE_W4];		break;
		case TNS_W5:	fPercentToMove = g_fSuperMeterPercentChange[SE_W5];		break;
		case TNS_Miss:	fPercentToMove = g_fSuperMeterPercentChange[SE_Miss];	break;
	}
	AddSuperMeterDelta( fPercentToMove );
}
Example #10
0
	FOREACH_CONST( TrailEntry, m_vEntries, e )
	{
		if( e->bSecret )
		{
			AddTo.Add( -1 );
			continue;
		}

		Song *pSong = e->pSong;
		ASSERT( pSong != NULL );
		switch( pSong->m_DisplayBPMType )
		{
		case DISPLAY_BPM_ACTUAL:
		case DISPLAY_BPM_SPECIFIED:
			pSong->GetDisplayBpms( AddTo );
			break;
		case DISPLAY_BPM_RANDOM:
			AddTo.Add( -1 );
			break;
		DEFAULT_FAIL( pSong->m_DisplayBPMType );
		}
	}
float ArrowEffects::GetXPos( const PlayerState* pPlayerState, int iColNum, float fYOffset ) 
{
	float fPixelOffsetFromCenter = 0; // fill this in below

	const Style* pStyle = GAMESTATE->GetCurrentStyle(pPlayerState->m_PlayerNumber);
	const float* fEffects = curr_options->m_fEffects;

	// TODO: Don't index by PlayerNumber.
	const Style::ColumnInfo* pCols = pStyle->m_ColumnInfo[pPlayerState->m_PlayerNumber];
	PerPlayerData &data = g_EffectData[pPlayerState->m_PlayerNumber];

	if( fEffects[PlayerOptions::EFFECT_TORNADO] != 0 )
	{
		const float fRealPixelOffset = pCols[iColNum].fXOffset * pPlayerState->m_NotefieldZoom;
		const float fPositionBetween = SCALE( fRealPixelOffset, data.m_fMinTornadoX[iColNum], data.m_fMaxTornadoX[iColNum], 
						     TORNADO_POSITION_SCALE_TO_LOW, TORNADO_POSITION_SCALE_TO_HIGH );
		float fRads = acosf( fPositionBetween );
		fRads += fYOffset * TORNADO_OFFSET_FREQUENCY / SCREEN_HEIGHT;

		const float fAdjustedPixelOffset = SCALE( RageFastCos(fRads), TORNADO_OFFSET_SCALE_FROM_LOW, TORNADO_OFFSET_SCALE_FROM_HIGH,
							 data.m_fMinTornadoX[iColNum], data.m_fMaxTornadoX[iColNum] );

		fPixelOffsetFromCenter += (fAdjustedPixelOffset - fRealPixelOffset) * fEffects[PlayerOptions::EFFECT_TORNADO];
	}

	if( fEffects[PlayerOptions::EFFECT_DRUNK] != 0 )
		fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_DRUNK] * 
			( RageFastCos( RageTimer::GetTimeSinceStartFast() + iColNum*DRUNK_COLUMN_FREQUENCY
				      + fYOffset*DRUNK_OFFSET_FREQUENCY/SCREEN_HEIGHT) * ARROW_SIZE*DRUNK_ARROW_MAGNITUDE );
	if( fEffects[PlayerOptions::EFFECT_FLIP] != 0 )
	{
		const int iFirstCol = 0;
		const int iLastCol = pStyle->m_iColsPerPlayer-1;
		const int iNewCol = SCALE( iColNum, iFirstCol, iLastCol, iLastCol, iFirstCol );
		const float fOldPixelOffset = pCols[iColNum].fXOffset * pPlayerState->m_NotefieldZoom;
		const float fNewPixelOffset = pCols[iNewCol].fXOffset * pPlayerState->m_NotefieldZoom;
		const float fDistance = fNewPixelOffset - fOldPixelOffset;
		fPixelOffsetFromCenter += fDistance * fEffects[PlayerOptions::EFFECT_FLIP];
	}
	if( fEffects[PlayerOptions::EFFECT_INVERT] != 0 )
		fPixelOffsetFromCenter += data.m_fInvertDistance[iColNum] * fEffects[PlayerOptions::EFFECT_INVERT];

	if( fEffects[PlayerOptions::EFFECT_BEAT] != 0 )
	{
		const float fShift = data.m_fBeatFactor*RageFastSin( fYOffset / BEAT_OFFSET_HEIGHT + PI/BEAT_PI_HEIGHT );
		fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_BEAT] * fShift;
	}

	if( fEffects[PlayerOptions::EFFECT_XMODE] != 0 )
	{
		// based off of code by v1toko for StepNXA, except it should work on
		// any gametype now.
		switch( pStyle->m_StyleType )
		{
			case StyleType_OnePlayerTwoSides:
			case StyleType_TwoPlayersSharedSides: // fall through?
				{
					// find the middle, and split based on iColNum
					// it's unknown if this will work for routine.
					const int iMiddleColumn = static_cast<int>(floor(pStyle->m_iColsPerPlayer/2.0f));
					if( iColNum > iMiddleColumn-1 )
						fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE]*-(fYOffset);
					else
						fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE]*fYOffset;
				}
				break;
			case StyleType_OnePlayerOneSide:
			case StyleType_TwoPlayersTwoSides: // fall through
				{
					// the code was the same for both of these cases in StepNXA.
					if( pPlayerState->m_PlayerNumber == PLAYER_2 )
						fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE]*-(fYOffset);
					else
						fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE]*fYOffset;
				}
				break;
			case StyleType_FourPlayersFourSides: // fall through
				{
					// the code was the same for both of these cases in StepNXA.
					if (pPlayerState->m_PlayerNumber == PLAYER_2)
						fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE] * -(fYOffset);
					else
						fPixelOffsetFromCenter += fEffects[PlayerOptions::EFFECT_XMODE] * fYOffset;
				}
				break;
			DEFAULT_FAIL(pStyle->m_StyleType);
		}
	}

	fPixelOffsetFromCenter += pCols[iColNum].fXOffset * pPlayerState->m_NotefieldZoom;

	if( fEffects[PlayerOptions::EFFECT_TINY] != 0 )
	{
		// Allow Tiny to pull tracks together, but not to push them apart.
		float fTinyPercent = fEffects[PlayerOptions::EFFECT_TINY];
		fTinyPercent = min( powf(TINY_PERCENT_BASE, fTinyPercent), (float)TINY_PERCENT_GATE );
		fPixelOffsetFromCenter *= fTinyPercent;
	}

	return fPixelOffsetFromCenter;
}
Example #12
0
bool SongCriteria::Matches( const Song *pSong ) const
{
    if( !m_sGroupName.empty()  &&  m_sGroupName != pSong->m_sGroupName )
        return false;

    if( UNLOCKMAN->SongIsLocked(pSong) & LOCKED_DISABLED )
        return false;

    if( m_bUseSongGenreAllowedList )
    {
        if( find(m_vsSongGenreAllowedList.begin(),m_vsSongGenreAllowedList.end(),pSong->m_sGenre) == m_vsSongGenreAllowedList.end() )
            return false;
    }

    switch( m_Selectable )
    {
        DEFAULT_FAIL(m_Selectable);
    case Selectable_Yes:
        if( UNLOCKMAN  &&  UNLOCKMAN->SongIsLocked(pSong) & LOCKED_SELECTABLE )
            return false;
        break;
    case Selectable_No:
        if( UNLOCKMAN  &&  !(UNLOCKMAN->SongIsLocked(pSong) & LOCKED_SELECTABLE) )
            return false;
        break;
    case Selectable_DontCare:
        break;
    }

    if( m_bUseSongAllowedList )
    {
        if( find(m_vpSongAllowedList.begin(),m_vpSongAllowedList.end(),pSong) == m_vpSongAllowedList.end() )
            return false;
    }

    if( m_iMaxStagesForSong != -1  &&  GAMESTATE->GetNumStagesMultiplierForSong(pSong) > m_iMaxStagesForSong )
        return false;

    switch( m_Tutorial )
    {
        DEFAULT_FAIL(m_Tutorial);
    case Tutorial_Yes:
        if( !pSong->IsTutorial() )
            return false;
        break;
    case Tutorial_No:
        if( pSong->IsTutorial() )
            return false;
        break;
    case Tutorial_DontCare:
        break;
    }

    switch( m_Locked )
    {
        DEFAULT_FAIL(m_Locked);
    case Locked_Locked:
        if( UNLOCKMAN  &&  !(UNLOCKMAN->SongIsLocked(pSong) & LOCKED_LOCK) )
            return false;
        break;
    case Locked_Unlocked:
        if( UNLOCKMAN  &&  UNLOCKMAN->SongIsLocked(pSong) & LOCKED_LOCK )
            return false;
        break;
    case Locked_DontCare:
        break;
    }

    return true;
}
Example #13
0
static NoteData ParseNoteData(RString &step1, RString &step2,
			      Steps &out, const RString &path)
{
	g_mapDanceNoteToNoteDataColumn.clear();
	switch( out.m_StepsType )
	{
		case StepsType_dance_single:
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 1;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 2;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 3;
			break;
		case StepsType_dance_double:
		case StepsType_dance_couple:
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 1;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 2;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 3;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_LEFT] = 4;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_DOWN] = 5;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_UP] = 6;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD2_RIGHT] = 7;
			break;
		case StepsType_dance_solo:
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_LEFT] = 0;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UPLEFT] = 1;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_DOWN] = 2;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UP] = 3;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_UPRIGHT] = 4;
			g_mapDanceNoteToNoteDataColumn[DANCE_NOTE_PAD1_RIGHT] = 5;
			break;
			DEFAULT_FAIL( out.m_StepsType );
	}
	
	NoteData newNoteData;
	newNoteData.SetNumTracks( g_mapDanceNoteToNoteDataColumn.size() );
	
	for( int pad=0; pad<2; pad++ )		// foreach pad
	{
		RString sStepData;
		switch( pad )
		{
			case 0:
				sStepData = step1;
				break;
			case 1:
				if( step2 == "" )	// no data
					continue;	// skip
				sStepData = step2;
				break;
				DEFAULT_FAIL( pad );
		}
		
		sStepData.Replace("\n", "");
		sStepData.Replace("\r", "");
		sStepData.Replace("\t", "");
		sStepData.Replace(" ", "");
		
		double fCurrentBeat = 0;
		double fCurrentIncrementer = 1.0/8 * BEATS_PER_MEASURE;
		
		for( size_t i=0; i<sStepData.size(); )
		{
			char c = sStepData[i++];
			switch( c )
			{
					// begins a series
				case '(':
					fCurrentIncrementer = 1.0/16 * BEATS_PER_MEASURE;
					break;
				case '[':
					fCurrentIncrementer = 1.0/24 * BEATS_PER_MEASURE;
					break;
				case '{':
					fCurrentIncrementer = 1.0/64 * BEATS_PER_MEASURE;
					break;
				case '`':
					fCurrentIncrementer = 1.0/192 * BEATS_PER_MEASURE;
					break;
					
					// ends a series
				case ')':
				case ']':
				case '}':
				case '\'':
				case '>':
					fCurrentIncrementer = 1.0/8 * BEATS_PER_MEASURE;
					break;
					
				default:	// this is a note character
				{
					if( c == '!' )
					{
						LOG->UserLog(
							     "Song file",
							     path,
							     "has an unexpected character: '!'." );
						continue;
					}
					
					bool jump = false;
					if( c == '<' )
					{
						/* Arr.  Is this a jump or a 1/192 marker? */
						if( Is192( sStepData, i ) )
						{
							fCurrentIncrementer = 1.0/192 * BEATS_PER_MEASURE;
							break;
						}
						
						/* It's a jump.
						 * We need to keep reading notes until we hit a >. */
						jump = true;
						i++;
					}
					
					const int iIndex = BeatToNoteRow( (float)fCurrentBeat );
					i--;
					do {
						c = sStepData[i++];
						
						if( jump && c == '>' )
							break;
						
						int iCol1, iCol2;
						DWIcharToNoteCol(
								 c,
								 (GameController)pad,
								 iCol1,
								 iCol2,
								 path );
						
						if( iCol1 != -1 )
							newNoteData.SetTapNote(iCol1,
									       iIndex,
									       TAP_ORIGINAL_TAP);
						if( iCol2 != -1 )
							newNoteData.SetTapNote(iCol2,
									       iIndex,
									       TAP_ORIGINAL_TAP);
						
						if(i>=sStepData.length())
						{
							break;
							//we ran out of data
							//while looking for the ending > mark
						}
						
						if( sStepData[i] == '!' )
						{
							i++;
							const char holdChar = sStepData[i++];
							
							DWIcharToNoteCol(holdChar,
									 (GameController)pad,
									 iCol1,
									 iCol2,
									 path );
							
							if( iCol1 != -1 )
								newNoteData.SetTapNote(iCol1,
										       iIndex,
										       TAP_ORIGINAL_HOLD_HEAD);
							if( iCol2 != -1 )
								newNoteData.SetTapNote(iCol2,
										       iIndex,
										       TAP_ORIGINAL_HOLD_HEAD);
						}
					}
					while( jump );
					fCurrentBeat += fCurrentIncrementer;
				}
					break;
			}
		}
	}
	
	/* Fill in iDuration. */
	for( int t=0; t<newNoteData.GetNumTracks(); ++t )
	{
		FOREACH_NONEMPTY_ROW_IN_TRACK( newNoteData, t, iHeadRow )
		{
			TapNote tn = newNoteData.GetTapNote( t, iHeadRow  );
			if( tn.type != TapNote::hold_head )
				continue;
			
			int iTailRow = iHeadRow;
			bool bFound = false;
			while( !bFound && newNoteData.GetNextTapNoteRowForTrack(t, iTailRow) )
			{
				const TapNote &TailTap = newNoteData.GetTapNote( t, iTailRow );
				if( TailTap.type == TapNote::empty )
					continue;
				
				newNoteData.SetTapNote( t, iTailRow, TAP_EMPTY );
				tn.iDuration = iTailRow - iHeadRow;
				newNoteData.SetTapNote( t, iHeadRow, tn );
				bFound = true;
			}
			
			if( !bFound )
			{
				/* The hold was never closed.  */
				LOG->UserLog("Song file",
					     path,
					     "failed to close a hold note in \"%s\" on track %i", 
					     DifficultyToString(out.GetDifficulty()).c_str(),
					     t);
				
				newNoteData.SetTapNote( t, iHeadRow, TAP_EMPTY );
			}
		}
	}
void NoteDataWithScoring::GetActualRadarValues(const NoteData &in,
	const PlayerStageStats &pss, float song_seconds, RadarValues& out)
{
	// Anybody editing this function should also examine
	// NoteDataUtil::CalculateRadarValues to make sure it handles things the
	// same way.
	// Some of this logic is similar or identical to
	// NoteDataUtil::CalculateRadarValues because I couldn't figure out a good
	// way to combine them into one. -Kyz
	PlayerNumber pn= pss.m_player_number;
	garv_state state;

	NoteData::all_tracks_const_iterator curr_note=
		in.GetTapNoteRangeAllTracks(0, MAX_NOTE_ROW);
	TimingData* timing= GAMESTATE->GetProcessedTimingData();
	// first_hittable_row and last_hittable_row exist so that
	// GetActualVoltageRadarValue can be passed the correct song length.
	// GetActualVoltageRadarValue scores based on the max combo, a full combo
	// is a full voltage score.  The song length is used instead of trying to
	// figure out the max combo for the song because rolls mean there isn't a
	// limit to the max combo. -Kyz
	int first_hittable_row= -1;
	int last_hittable_row= -1;
	bool tick_holds= GAMESTATE->GetCurrentGame()->m_bTickHolds;

	while(!curr_note.IsAtEnd())
	{
		if(curr_note.Row() != state.curr_row)
		{
			DoRowEndRadarActualCalc(state, out);
			state.curr_row= curr_note.Row();
			state.num_notes_on_curr_row= 0;
			state.num_holds_on_curr_row= 0;
			state.judgable= timing->IsJudgableAtRow(state.curr_row);
			for(size_t n= 0; n < state.hold_ends.size(); ++n)
			{
				if(state.hold_ends[n].end_row < state.curr_row)
				{
					state.hold_ends.erase(state.hold_ends.begin() + n);
					--n;
				}
			}
			state.last_tns_on_row= TapNoteScore_Invalid;
			state.last_time_on_row= -9999;
			state.worst_tns_on_row= TapNoteScore_Invalid;
		}
		bool for_this_player= curr_note->pn == pn || pn == PLAYER_INVALID ||
			curr_note->pn == PLAYER_INVALID;
		if(state.judgable && for_this_player)
		{
			switch(curr_note->type)
			{
				case TapNoteType_HoldTail:
					// If there are tick holds, then the hold tail needs to be counted
					// in last_hittable_row because that's where the combo will end.
					// -Kyz
					if(tick_holds)
					{
						UpdateHittable(state.curr_row, first_hittable_row, last_hittable_row);
					}
					break;
				case TapNoteType_Tap:
				case TapNoteType_HoldHead:
					// HoldTails and Attacks are counted by IsTap.  But it doesn't
					// make sense to count HoldTails as hittable notes. -Kyz
				case TapNoteType_Attack:
				case TapNoteType_Lift:
					UpdateHittable(state.curr_row, first_hittable_row, last_hittable_row);
					++state.num_notes_on_curr_row;
					state.notes_hit_for_stream+= (curr_note->result.tns >= state.stream_tns);
					state.notes_hit+= (curr_note->result.tns >= state.taps_tns);
					if(curr_note->result.tns < state.worst_tns_on_row)
					{
						state.worst_tns_on_row= curr_note->result.tns;
					}
					if(curr_note->result.fTapNoteOffset > state.last_time_on_row)
					{
						state.last_time_on_row= curr_note->result.fTapNoteOffset;
						state.last_tns_on_row= curr_note->result.tns;
					}
					if(curr_note->type == TapNoteType_HoldHead)
					{
						if(curr_note->subType == TapNoteSubType_Hold)
						{
							state.holds_held+= (curr_note->HoldResult.hns == HNS_Held);
						}
						else if(curr_note->subType == TapNoteSubType_Roll)
						{
							state.rolls_held+= (curr_note->HoldResult.hns == HNS_Held);
						}
						state.hold_ends.push_back(
							hold_status(state.curr_row + curr_note->iDuration,
								curr_note->HoldResult.iLastHeldRow));
						++state.num_holds_on_curr_row;
					}
					else if(curr_note->type == TapNoteType_Lift)
					{
						state.lifts_hit+= (curr_note->result.tns >= state.lifts_tns);
					}
					break;
				case TapNoteType_Mine:
					state.mines_avoided+= (curr_note->result.tns == TNS_AvoidMine);
					break;
				case TapNoteType_Fake:
				default:
					break;
			}
		}
		++curr_note;
	}
	DoRowEndRadarActualCalc(state, out);

	// ScreenGameplay passes in the RadarValues that were calculated by
	// NoteDataUtil::CalculateRadarValues, so those are reused here. -Kyz
	int note_count= out[RadarCategory_Notes];
	int jump_count= out[RadarCategory_Jumps];
	int hold_count= out[RadarCategory_Holds];
	int tap_count= out[RadarCategory_TapsAndHolds];
	float hittable_steps_length= max(0, 
		timing->GetElapsedTimeFromBeat(NoteRowToBeat(last_hittable_row)) -
		timing->GetElapsedTimeFromBeat(NoteRowToBeat(first_hittable_row)));
	// The for loop and the assert are used to ensure that all fields of 
	// RadarValue get set in here.
	FOREACH_ENUM(RadarCategory, rc)
	{
		switch(rc)
		{
			case RadarCategory_Stream:
				out[rc]= clamp(float(state.notes_hit_for_stream) / note_count, 0.0f, 1.0f);
				break;
			case RadarCategory_Voltage:
				out[rc]= GetActualVoltageRadarValue(in, hittable_steps_length, pss);
				break;
			case RadarCategory_Air:
				out[rc]= clamp(float(state.jumps_hit_for_air) / jump_count, 0.0f, 1.0f);
				break;
			case RadarCategory_Freeze:
				out[rc]= clamp(float(state.holds_held) / hold_count, 0.0f, 1.0f);
				break;
			case RadarCategory_Chaos:
				out[rc]= GetActualChaosRadarValue(in, song_seconds, pss);
				break;
			case RadarCategory_TapsAndHolds:
				out[rc]= state.taps_hit;
				break;
			case RadarCategory_Jumps:
				out[rc]= state.jumps_hit;
				break;
			case RadarCategory_Holds:
				out[rc]= state.holds_held;
				break;
			case RadarCategory_Mines:
				out[rc]= state.mines_avoided;
				break;
			case RadarCategory_Hands:
				out[rc]= state.hands_hit;
				break;
			case RadarCategory_Rolls:
				out[rc]= state.rolls_held;
				break;
			case RadarCategory_Lifts:
				out[rc]= state.lifts_hit;
				break;
			case RadarCategory_Fakes:
				out[rc]= out[rc];
				break;
			case RadarCategory_Notes:
				out[rc]= state.notes_hit;
				break;
			DEFAULT_FAIL(rc);
		}
	}
}