2002-04-16 17:31:00 +00:00
# include "stdafx.h"
/*
-----------------------------------------------------------------------------
2002-07-27 19:29:51 +00:00
Class: NoteData
2002-04-16 17:31:00 +00:00
2002-07-02 00:27:58 +00:00
Desc: See header.
2002-04-16 17:31:00 +00:00
2002-05-19 01:59:48 +00:00
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
2002-04-16 17:31:00 +00:00
Chris Danford
-----------------------------------------------------------------------------
*/
# include "NoteData.h"
# include "RageUtil.h"
2002-05-01 19:14:55 +00:00
# include "GameConstantsAndTypes.h"
2002-07-23 01:41:40 +00:00
# include "PrefsManager.h"
2002-04-16 17:31:00 +00:00
# include "ArrowEffects.h"
# include "PrefsManager.h"
2002-05-01 19:14:55 +00:00
# include "GameConstantsAndTypes.h"
2002-07-27 19:29:51 +00:00
# include "RageException.h"
2002-08-01 20:30:40 +00:00
# include "GameState.h"
2002-06-14 22:25:22 +00:00
2002-04-16 17:31:00 +00:00
NoteData : : NoteData ( )
2002-06-24 22:04:31 +00:00
{
Init ( ) ;
}
void NoteData : : Init ( )
2002-04-16 17:31:00 +00:00
{
2002-07-03 21:27:26 +00:00
memset ( m_TapNotes , ' 0 ' , MAX_NOTE_TRACKS * MAX_TAP_NOTE_ROWS * sizeof ( m_TapNotes [ 0 ] [ 0 ] ) ) ;
2002-04-16 17:31:00 +00:00
m_iNumTracks = 0 ;
m_iNumHoldNotes = 0 ;
}
2002-05-19 01:59:48 +00:00
NoteData : : ~ NoteData ( )
{
}
2002-07-03 03:13:13 +00:00
void NoteData : : LoadFromSMNoteDataString ( CString sSMNoteData )
{
2002-07-28 20:28:37 +00:00
int iNumTracks = m_iNumTracks ;
NoteData : : Init ( ) ;
m_iNumTracks = iNumTracks ;
2002-07-03 03:13:13 +00:00
// strip comments out of sSMNoteData
while ( sSMNoteData . Find ( " // " ) ! = - 1 )
{
int iIndexCommentStart = sSMNoteData . Find ( " // " ) ;
int iIndexCommentEnd = sSMNoteData . Find ( " \n " , iIndexCommentStart ) ;
if ( iIndexCommentEnd = = - 1 ) // comment doesn't have an end?
sSMNoteData . Delete ( iIndexCommentStart , 2 ) ;
else
sSMNoteData . Delete ( iIndexCommentStart , iIndexCommentEnd - iIndexCommentStart ) ;
}
CStringArray asMeasures ;
split ( sSMNoteData , " , " , asMeasures , true ) ; // ignore empty is important
for ( int m = 0 ; m < asMeasures . GetSize ( ) ; m + + ) // foreach measure
{
CString & sMeasureString = asMeasures [ m ] ;
sMeasureString . TrimLeft ( ) ;
sMeasureString . TrimRight ( ) ;
CStringArray asMeasureLines ;
split ( sMeasureString , " \n " , asMeasureLines , true ) ; // ignore empty is important
2002-07-03 21:27:26 +00:00
//ASSERT( asMeasureLines.GetSize() == 4 ||
// asMeasureLines.GetSize() == 8 ||
// asMeasureLines.GetSize() == 12 ||
// asMeasureLines.GetSize() == 16 );
2002-07-03 03:13:13 +00:00
for ( int l = 0 ; l < asMeasureLines . GetSize ( ) ; l + + )
{
CString & sMeasureLine = asMeasureLines [ l ] ;
sMeasureLine . TrimLeft ( ) ;
sMeasureLine . TrimRight ( ) ;
const float fPercentIntoMeasure = l / ( float ) asMeasureLines . GetSize ( ) ;
const float fBeat = ( m + fPercentIntoMeasure ) * BEATS_PER_MEASURE ;
const int iIndex = BeatToNoteRow ( fBeat ) ;
if ( m_iNumTracks ! = sMeasureLine . GetLength ( ) )
throw RageException ( " Actual number of note columns (%d) is different from the NotesType (%d). " , m_iNumTracks , sMeasureLine . GetLength ( ) ) ;
for ( int c = 0 ; c < sMeasureLine . GetLength ( ) ; c + + )
m_TapNotes [ c ] [ iIndex ] = sMeasureLine [ c ] ;
}
}
2002-07-03 21:27:26 +00:00
this - > Convert2sAnd3sToHoldNotes ( ) ;
2002-07-03 03:13:13 +00:00
}
CString NoteData : : GetSMNoteDataString ( )
{
this - > ConvertHoldNotesTo2sAnd3s ( ) ;
float fLastBeat = GetLastBeat ( ) ;
int iLastMeasure = int ( fLastBeat / BEATS_PER_MEASURE ) ;
CStringArray asMeasureStrings ;
for ( int m = 0 ; m < = iLastMeasure ; m + + ) // foreach measure
{
2002-08-23 20:18:29 +00:00
NoteType nt = GetSmallestNoteTypeForMeasure ( m ) ;
float fBeatSpacing = NoteTypeToBeat ( nt ) ;
int iRowSpacing = roundf ( fBeatSpacing * ROWS_PER_BEAT ) ;
2002-07-03 03:13:13 +00:00
CStringArray asMeasureLines ;
asMeasureLines . Add ( ssprintf ( " // measure %d " , m + 1 ) ) ;
2002-07-11 19:02:26 +00:00
2002-08-23 20:18:29 +00:00
const int iMeasureStartRow = m * ROWS_PER_MEASURE ;
const int iMeasureLastRow = ( m + 1 ) * ROWS_PER_MEASURE - 1 ;
for ( int r = iMeasureStartRow ; r < = iMeasureLastRow ; r + = iRowSpacing )
2002-07-03 03:13:13 +00:00
{
2002-07-11 19:02:26 +00:00
char szLineString [ MAX_NOTE_TRACKS ] ;
for ( int t = 0 ; t < m_iNumTracks ; t + + )
2002-08-23 20:18:29 +00:00
szLineString [ t ] = m_TapNotes [ t ] [ r ] ;
2002-07-11 19:02:26 +00:00
szLineString [ t ] = ' \0 ' ;
asMeasureLines . Add ( szLineString ) ;
2002-07-03 03:13:13 +00:00
}
2002-07-11 19:02:26 +00:00
2002-07-03 03:13:13 +00:00
CString sMeasureString = join ( " \n " , asMeasureLines ) ;
asMeasureStrings . Add ( sMeasureString ) ;
}
this - > Convert2sAnd3sToHoldNotes ( ) ;
return join ( " \n , " , asMeasureStrings ) ;
}
2002-05-27 08:23:27 +00:00
void NoteData : : ClearRange ( int iNoteIndexBegin , int iNoteIndexEnd )
2002-04-16 17:31:00 +00:00
{
// delete old TapNotes in the range
for ( int i = iNoteIndexBegin ; i < = iNoteIndexEnd ; i + + )
{
for ( int c = 0 ; c < m_iNumTracks ; c + + )
m_TapNotes [ c ] [ i ] = ' 0 ' ;
}
// delete old HoldNotes in the range
int iIndexRead , iIndexWrite = 0 ;
const int iOrigNumHoldNotes = m_iNumHoldNotes ;
for ( iIndexRead = 0 ; iIndexRead < iOrigNumHoldNotes ; iIndexRead + + )
{
HoldNote & hn = m_HoldNotes [ iIndexRead ] ;
2002-08-13 23:26:46 +00:00
int iHoldStartIndex = BeatToNoteRow ( hn . m_fStartBeat ) ;
int iHoldEndIndex = BeatToNoteRow ( hn . m_fEndBeat ) ;
if ( ( iHoldStartIndex > = iNoteIndexBegin & & iHoldStartIndex < = iNoteIndexEnd ) | |
( iHoldEndIndex > = iNoteIndexBegin & & iHoldEndIndex < = iNoteIndexEnd ) ) // overlap (kinda)
2002-04-16 17:31:00 +00:00
{
; // do nothing
}
else // ! overlap
{
m_HoldNotes [ iIndexWrite ] = m_HoldNotes [ iIndexRead ] ;
iIndexWrite + + ;
}
}
m_iNumHoldNotes = iIndexWrite ;
}
2002-06-29 11:59:09 +00:00
void NoteData : : CopyRange ( NoteData * pFrom , int iFromIndexBegin , int iFromIndexEnd , int iToIndexBegin )
2002-04-16 17:31:00 +00:00
{
ASSERT ( pFrom - > m_iNumTracks = = m_iNumTracks ) ;
2002-06-29 11:59:09 +00:00
if ( iToIndexBegin = = - 1 )
2002-07-02 00:27:58 +00:00
iToIndexBegin = 0 ;
2002-06-29 11:59:09 +00:00
pFrom - > ConvertHoldNotesTo2sAnd3s ( ) ;
int f , t ;
2002-04-16 17:31:00 +00:00
// copy recorded TapNotes
2002-06-29 11:59:09 +00:00
f = iFromIndexBegin ;
t = iToIndexBegin ;
while ( f < = iFromIndexEnd )
2002-04-16 17:31:00 +00:00
{
for ( int c = 0 ; c < m_iNumTracks ; c + + )
2002-06-29 11:59:09 +00:00
m_TapNotes [ c ] [ t ] = pFrom - > m_TapNotes [ c ] [ f ] ;
f + + ;
t + + ;
2002-04-16 17:31:00 +00:00
}
2002-06-29 11:59:09 +00:00
pFrom - > Convert2sAnd3sToHoldNotes ( ) ;
this - > Convert2sAnd3sToHoldNotes ( ) ;
2002-04-16 17:31:00 +00:00
}
void NoteData : : AddHoldNote ( HoldNote add )
{
int i ;
// look for other hold notes that overlap and merge them
for ( i = 0 ; i < m_iNumHoldNotes ; i + + ) // for each HoldNote
{
HoldNote & other = m_HoldNotes [ i ] ;
if ( add . m_iTrack = = other . m_iTrack & & // the notes correspond
2002-08-13 23:26:46 +00:00
other . m_fStartBeat < = add . m_fEndBeat & &
other . m_fEndBeat > = add . m_fStartBeat ) // these HoldNotes overlap (this isn't entirely correct!)
2002-04-16 17:31:00 +00:00
{
2002-08-13 23:26:46 +00:00
add . m_fStartBeat = min ( add . m_fStartBeat , other . m_fStartBeat ) ;
add . m_fEndBeat = max ( add . m_fEndBeat , other . m_fEndBeat ) ;
2002-04-16 17:31:00 +00:00
// delete this HoldNote
for ( int j = i ; j < m_iNumHoldNotes - 1 ; j + + )
m_HoldNotes [ j ] = m_HoldNotes [ j + 1 ] ;
2002-08-13 23:26:46 +00:00
2002-04-16 17:31:00 +00:00
m_iNumHoldNotes - - ;
}
}
2002-08-13 23:26:46 +00:00
int iAddStartIndex = BeatToNoteRow ( add . m_fStartBeat ) ;
int iAddEndIndex = BeatToNoteRow ( add . m_fEndBeat ) ;
2002-04-16 17:31:00 +00:00
// delete TapNotes under this HoldNote
2002-08-13 23:26:46 +00:00
for ( i = iAddStartIndex + 1 ; i < = iAddEndIndex ; i + + )
2002-04-16 17:31:00 +00:00
m_TapNotes [ add . m_iTrack ] [ i ] = ' 0 ' ;
2002-06-14 22:25:22 +00:00
// add a tap note at the start of this hold
2002-08-13 23:26:46 +00:00
m_TapNotes [ add . m_iTrack ] [ iAddStartIndex ] = ' 2 ' ; // '2' means a Hold begin marker. Don't draw this, but do grade it.
2002-06-14 22:25:22 +00:00
2002-07-11 19:02:26 +00:00
ASSERT ( m_iNumHoldNotes < MAX_HOLD_NOTES ) ;
2002-04-16 17:31:00 +00:00
m_HoldNotes [ m_iNumHoldNotes + + ] = add ;
}
2002-08-13 23:26:46 +00:00
void NoteData : : RemoveHoldNote ( int iHoldIndex )
2002-04-16 17:31:00 +00:00
{
2002-08-17 06:44:04 +00:00
ASSERT ( iHoldIndex > = 0 & & iHoldIndex < m_iNumHoldNotes ) ;
2002-08-13 23:26:46 +00:00
HoldNote & hn = m_HoldNotes [ iHoldIndex ] ;
int iHoldStartIndex = BeatToNoteRow ( hn . m_fStartBeat ) ;
int iHoldEndIndex = BeatToNoteRow ( hn . m_fEndBeat ) ;
2002-04-16 17:31:00 +00:00
2002-06-14 22:25:22 +00:00
// delete a tap note at the start of this hold
2002-08-13 23:26:46 +00:00
m_TapNotes [ hn . m_iTrack ] [ iHoldStartIndex ] = ' 0 ' ;
2002-06-14 22:25:22 +00:00
// remove from list
2002-08-13 23:26:46 +00:00
for ( int j = iHoldIndex ; j < m_iNumHoldNotes - 1 ; j + + )
2002-04-16 17:31:00 +00:00
m_HoldNotes [ j ] = m_HoldNotes [ j + 1 ] ;
2002-08-13 23:26:46 +00:00
2002-04-16 17:31:00 +00:00
m_iNumHoldNotes - - ;
}
2002-06-24 22:04:31 +00:00
bool NoteData : : IsThereANoteAtRow ( int iRow )
{
for ( int t = 0 ; t < m_iNumTracks ; t + + )
if ( m_TapNotes [ t ] [ iRow ] ! = ' 0 ' )
return true ;
2002-08-13 23:26:46 +00:00
// There is a tap note at the beginning of every HoldNote start
//
// for( int i=0; i<m_iNumHoldNotes; i++ ) // for each HoldNote
// if( m_HoldNotes[i].m_iStartIndex == NoteRowToBeatRow(iRow) )
// return true;
2002-06-24 22:04:31 +00:00
return false ;
}
2002-07-11 19:02:26 +00:00
int NoteData : : GetFirstRow ( )
2002-04-16 17:31:00 +00:00
{
2002-08-13 23:26:46 +00:00
return BeatToNoteRow ( GetFirstBeat ( ) ) ;
}
float NoteData : : GetFirstBeat ( )
{
float fEarliestBeatFoundSoFar = MAX_BEATS ;
2002-04-16 17:31:00 +00:00
int i ;
2002-08-13 23:26:46 +00:00
for ( i = 0 ; i < MAX_TAP_NOTE_ROWS ; i + + )
2002-07-11 19:02:26 +00:00
{
if ( ! IsRowEmpty ( i ) )
{
2002-08-13 23:26:46 +00:00
fEarliestBeatFoundSoFar = NoteRowToBeat ( i ) ;
2002-07-11 19:02:26 +00:00
break ;
}
}
2002-04-16 17:31:00 +00:00
for ( i = 0 ; i < m_iNumHoldNotes ; i + + )
2002-08-13 23:26:46 +00:00
if ( m_HoldNotes [ i ] . m_fStartBeat < fEarliestBeatFoundSoFar )
fEarliestBeatFoundSoFar = m_HoldNotes [ i ] . m_fStartBeat ;
2002-04-16 17:31:00 +00:00
2002-07-11 19:02:26 +00:00
2002-08-13 23:26:46 +00:00
if ( fEarliestBeatFoundSoFar = = MAX_BEATS ) // there are no notes
return 0 ;
else
return fEarliestBeatFoundSoFar ;
2002-07-11 19:02:26 +00:00
}
2002-08-13 23:26:46 +00:00
int NoteData : : GetLastRow ( )
2002-07-11 19:02:26 +00:00
{
2002-08-13 23:26:46 +00:00
return BeatToNoteRow ( GetLastBeat ( ) ) ;
2002-07-11 19:02:26 +00:00
}
2002-08-13 23:26:46 +00:00
float NoteData : : GetLastBeat ( )
2002-07-11 19:02:26 +00:00
{
2002-08-13 23:26:46 +00:00
float fOldestBeatFoundSoFar = 0 ;
2002-07-11 19:02:26 +00:00
int i ;
2002-08-13 23:26:46 +00:00
for ( i = MAX_TAP_NOTE_ROWS - 1 ; i > = 0 ; i - - ) // iterate back to front
2002-04-16 17:31:00 +00:00
{
2002-07-11 19:02:26 +00:00
if ( ! IsRowEmpty ( i ) )
2002-04-16 17:31:00 +00:00
{
2002-08-13 23:26:46 +00:00
fOldestBeatFoundSoFar = NoteRowToBeat ( i ) ;
2002-07-11 19:02:26 +00:00
break ;
2002-04-16 17:31:00 +00:00
}
}
2002-07-11 19:02:26 +00:00
for ( i = 0 ; i < m_iNumHoldNotes ; i + + )
{
2002-08-13 23:26:46 +00:00
if ( m_HoldNotes [ i ] . m_fEndBeat > fOldestBeatFoundSoFar )
fOldestBeatFoundSoFar = m_HoldNotes [ i ] . m_fEndBeat ;
2002-07-11 19:02:26 +00:00
}
2002-04-16 17:31:00 +00:00
2002-08-13 23:26:46 +00:00
return fOldestBeatFoundSoFar ;
2002-04-16 17:31:00 +00:00
}
2002-05-19 01:59:48 +00:00
int NoteData : : GetNumTapNotes ( const float fStartBeat , const float fEndBeat )
2002-04-16 17:31:00 +00:00
{
2002-06-24 22:04:31 +00:00
int iNumNotes = 0 ;
2002-04-16 17:31:00 +00:00
2002-05-19 01:59:48 +00:00
int iStartIndex = BeatToNoteRow ( fStartBeat ) ;
int iEndIndex = BeatToNoteRow ( fEndBeat ) ;
for ( int i = iStartIndex ; i < min ( iEndIndex , MAX_TAP_NOTE_ROWS ) ; i + + )
2002-04-28 20:42:32 +00:00
for ( int t = 0 ; t < m_iNumTracks ; t + + )
2002-04-16 17:31:00 +00:00
if ( m_TapNotes [ t ] [ i ] ! = ' 0 ' )
2002-06-24 22:04:31 +00:00
iNumNotes + + ;
2002-04-16 17:31:00 +00:00
2002-06-24 22:04:31 +00:00
return iNumNotes ;
2002-04-16 17:31:00 +00:00
}
2002-05-19 01:59:48 +00:00
int NoteData : : GetNumDoubles ( const float fStartBeat , const float fEndBeat )
{
int iNumDoubles = 0 ;
int iStartIndex = BeatToNoteRow ( fStartBeat ) ;
int iEndIndex = BeatToNoteRow ( fEndBeat ) ;
for ( int i = iStartIndex ; i < min ( iEndIndex , MAX_TAP_NOTE_ROWS ) ; i + + )
{
int iNumNotesThisIndex = 0 ;
for ( int t = 0 ; t < m_iNumTracks ; t + + )
{
if ( m_TapNotes [ t ] [ i ] ! = ' 0 ' )
iNumNotesThisIndex + + ;
}
if ( iNumNotesThisIndex > = 2 )
iNumDoubles + + ;
}
return iNumDoubles ;
}
int NoteData : : GetNumHoldNotes ( const float fStartBeat , const float fEndBeat )
{
2002-06-24 22:04:31 +00:00
int iNumHolds = 0 ;
2002-05-19 01:59:48 +00:00
for ( int i = 0 ; i < m_iNumHoldNotes ; i + + )
{
2002-06-24 22:04:31 +00:00
HoldNote & hn = m_HoldNotes [ i ] ;
2002-08-13 23:26:46 +00:00
if ( fStartBeat < = hn . m_fStartBeat & & hn . m_fEndBeat < = fEndBeat )
2002-06-24 22:04:31 +00:00
iNumHolds + + ;
2002-05-19 01:59:48 +00:00
}
2002-06-24 22:04:31 +00:00
return iNumHolds ;
2002-04-16 17:31:00 +00:00
}
2002-06-14 22:25:22 +00:00
int NoteData : : GetPossibleDancePoints ( )
{
//Each song has a certain number of "Dance Points" assigned to it. For regular arrows, this is 2 per arrow. For freeze arrows, it is 6 per arrow. When you add this all up, you get the maximum number of possible "Dance Points".
//
//Your "Dance Points" are calculated as follows:
//
//A "Perfect" is worth 2 points
//A "Great" is worth 1 points
//A "Good" is worth 0 points
//A "Boo" will subtract 4 points
//A "Miss" will subtract 8 points
//An "OK" (Successful Freeze step) will add 6 points
//A "NG" (Unsuccessful Freeze step) is worth 0 points
return GetNumTapNotes ( ) * TapNoteScoreToDancePoints ( TNS_PERFECT ) +
GetNumHoldNotes ( ) * HoldNoteScoreToDancePoints ( HNS_OK ) ;
}
2002-04-16 17:31:00 +00:00
void NoteData : : CropToLeftSide ( )
{
int iFirstRightSideColumn = 4 ;
int iLastRightSideColumn = 7 ;
// clear out the right half
for ( int c = iFirstRightSideColumn ; c < = iLastRightSideColumn ; c + + )
{
2002-05-19 01:59:48 +00:00
for ( int i = 0 ; i < MAX_TAP_NOTE_ROWS ; i + + ) // foreach TapNote
2002-04-16 17:31:00 +00:00
m_TapNotes [ c ] [ i ] = ' 0 ' ;
}
for ( int i = m_iNumHoldNotes - 1 ; i > = 0 ; i - - ) // foreach HoldNote
{
if ( c > = iFirstRightSideColumn )
{
// delete this HoldNote by shifting everything down
for ( int j = i ; j < m_iNumHoldNotes - 1 ; j + + )
m_HoldNotes [ j ] = m_HoldNotes [ j + 1 ] ;
m_iNumHoldNotes - - ;
}
}
}
void NoteData : : CropToRightSide ( )
{
int iFirstRightSideColumn = 4 ;
int iLastRightSideColumn = 7 ;
int c ;
// clear out the left half
for ( c = 0 ; c < iFirstRightSideColumn ; c + + )
{
2002-05-19 01:59:48 +00:00
for ( int i = 0 ; i < MAX_TAP_NOTE_ROWS ; i + + ) // foreach TapNote
2002-04-16 17:31:00 +00:00
m_TapNotes [ c ] [ i ] = ' 0 ' ;
}
// copy the right side into the left
for ( c = iFirstRightSideColumn ; c < = iLastRightSideColumn ; c + + )
{
2002-05-19 01:59:48 +00:00
for ( int i = 0 ; i < MAX_TAP_NOTE_ROWS ; i + + ) // foreach TapNote
2002-04-16 17:31:00 +00:00
{
m_TapNotes [ c - 4 ] [ i ] = m_TapNotes [ c ] [ i ] ;
m_TapNotes [ c ] [ i ] = ' 0 ' ;
}
}
for ( int i = m_iNumHoldNotes - 1 ; i > = 0 ; i - - ) // foreach HoldNote
{
HoldNote & hn = m_HoldNotes [ i ] ;
if ( hn . m_iTrack < iFirstRightSideColumn )
{
// delete this HoldNote by shifting everything down
RemoveHoldNote ( i ) ;
}
else
{
m_HoldNotes [ i ] . m_iTrack - = 4 ;
}
}
}
void NoteData : : RemoveHoldNotes ( )
{
// turn all the HoldNotes into TapNotes
for ( int i = 0 ; i < m_iNumHoldNotes ; i + + )
{
HoldNote & hn = m_HoldNotes [ i ] ;
2002-08-13 23:26:46 +00:00
int iIndex = BeatToNoteRow ( hn . m_fStartBeat ) ;
2002-04-16 17:31:00 +00:00
int iCol = hn . m_iTrack ;
m_TapNotes [ iCol ] [ iIndex ] = ' 1 ' ;
}
// Remove all HoldNotes
m_iNumHoldNotes = 0 ;
}
void NoteData : : Turn ( PlayerOptions : : TurnType tt )
{
2002-08-01 20:30:40 +00:00
int iTakeFromTrack [ MAX_NOTE_TRACKS ] ; // New track "t" will take from old track iTakeFromTrack[t]
2002-04-16 17:31:00 +00:00
2002-08-02 09:31:06 +00:00
int t ;
2002-04-16 17:31:00 +00:00
switch ( tt )
{
case PlayerOptions : : TURN_NONE :
2002-08-01 20:30:40 +00:00
return ; // nothing to do
2002-04-16 17:31:00 +00:00
case PlayerOptions : : TURN_MIRROR :
2002-08-02 09:31:06 +00:00
for ( t = 0 ; t < m_iNumTracks ; t + + )
2002-08-01 20:30:40 +00:00
iTakeFromTrack [ t ] = m_iNumTracks - t - 1 ;
2002-04-16 17:31:00 +00:00
break ;
case PlayerOptions : : TURN_LEFT :
2002-08-01 20:30:40 +00:00
case PlayerOptions : : TURN_RIGHT : // HACK: TurnRight does the same thing as TurnLeft. I'll fix this later...
// Chris: Handling each NotesType case is a terrible way to do this, but oh well...
switch ( GAMESTATE - > GetCurrentStyleDef ( ) - > m_NotesType )
{
case NOTES_TYPE_DANCE_SINGLE :
case NOTES_TYPE_DANCE_DOUBLE :
case NOTES_TYPE_DANCE_COUPLE :
iTakeFromTrack [ 0 ] = 2 ;
iTakeFromTrack [ 1 ] = 0 ;
iTakeFromTrack [ 2 ] = 3 ;
iTakeFromTrack [ 3 ] = 1 ;
iTakeFromTrack [ 4 ] = 6 ;
iTakeFromTrack [ 5 ] = 4 ;
iTakeFromTrack [ 6 ] = 7 ;
iTakeFromTrack [ 7 ] = 5 ;
break ;
case NOTES_TYPE_DANCE_SOLO :
iTakeFromTrack [ 0 ] = 5 ;
iTakeFromTrack [ 1 ] = 4 ;
iTakeFromTrack [ 2 ] = 0 ;
iTakeFromTrack [ 3 ] = 3 ;
iTakeFromTrack [ 4 ] = 1 ;
iTakeFromTrack [ 5 ] = 2 ;
break ;
case NOTES_TYPE_PUMP_SINGLE :
case NOTES_TYPE_PUMP_DOUBLE :
2002-08-22 22:59:19 +00:00
case NOTES_TYPE_PUMP_COUPLE :
2002-08-01 20:30:40 +00:00
iTakeFromTrack [ 0 ] = 1 ;
iTakeFromTrack [ 1 ] = 3 ;
iTakeFromTrack [ 2 ] = 2 ;
iTakeFromTrack [ 3 ] = 4 ;
iTakeFromTrack [ 4 ] = 0 ;
iTakeFromTrack [ 5 ] = 6 ;
iTakeFromTrack [ 6 ] = 8 ;
iTakeFromTrack [ 7 ] = 7 ;
iTakeFromTrack [ 8 ] = 9 ;
iTakeFromTrack [ 9 ] = 5 ;
break ;
case NOTES_TYPE_EZ2_SINGLE :
case NOTES_TYPE_EZ2_SINGLE_HARD :
case NOTES_TYPE_EZ2_DOUBLE :
case NOTES_TYPE_EZ2_REAL :
// identity transform. What should we do here?
iTakeFromTrack [ 0 ] = 0 ;
iTakeFromTrack [ 1 ] = 1 ;
iTakeFromTrack [ 2 ] = 2 ;
iTakeFromTrack [ 3 ] = 3 ;
iTakeFromTrack [ 4 ] = 4 ;
iTakeFromTrack [ 5 ] = 5 ;
iTakeFromTrack [ 6 ] = 6 ;
iTakeFromTrack [ 7 ] = 7 ;
iTakeFromTrack [ 8 ] = 8 ;
iTakeFromTrack [ 9 ] = 9 ;
iTakeFromTrack [ 10 ] = 10 ;
iTakeFromTrack [ 11 ] = 11 ;
break ;
}
2002-04-16 17:31:00 +00:00
break ;
case PlayerOptions : : TURN_SHUFFLE :
2002-08-01 20:30:40 +00:00
CArray < int , int > aiTracksLeftToMap ;
2002-08-02 09:31:06 +00:00
for ( t = 0 ; t < m_iNumTracks ; t + + )
2002-08-01 20:30:40 +00:00
aiTracksLeftToMap . Add ( t ) ;
2002-04-16 17:31:00 +00:00
2002-08-02 09:31:06 +00:00
for ( t = 0 ; t < m_iNumTracks ; t + + )
2002-04-16 17:31:00 +00:00
{
2002-08-01 20:30:40 +00:00
int iRandTrackIndex = rand ( ) % aiTracksLeftToMap . GetSize ( ) ;
int iRandTrack = aiTracksLeftToMap [ iRandTrackIndex ] ;
aiTracksLeftToMap . RemoveAt ( iRandTrackIndex ) ;
iTakeFromTrack [ t ] = iRandTrack ;
2002-04-16 17:31:00 +00:00
}
break ;
}
2002-07-02 00:27:58 +00:00
NoteData tempNoteData ; // write into here as we tranform
tempNoteData . m_iNumTracks = m_iNumTracks ;
2002-04-16 17:31:00 +00:00
2002-08-01 20:30:40 +00:00
this - > ConvertHoldNotesTo2sAnd3s ( ) ;
2002-04-16 17:31:00 +00:00
2002-08-01 20:30:40 +00:00
// transform notes
2002-08-02 09:31:06 +00:00
for ( t = 0 ; t < m_iNumTracks ; t + + )
2002-08-01 20:30:40 +00:00
for ( int r = 0 ; r < MAX_TAP_NOTE_ROWS ; r + + )
tempNoteData . m_TapNotes [ t ] [ r ] = m_TapNotes [ iTakeFromTrack [ t ] ] [ r ] ;
2002-04-16 17:31:00 +00:00
2002-08-01 20:30:40 +00:00
this - > CopyAll ( & tempNoteData ) ; // copy note data from newData back into this
this - > Convert2sAnd3sToHoldNotes ( ) ;
2002-04-16 17:31:00 +00:00
}
void NoteData : : MakeLittle ( )
{
// filter out all non-quarter notes
2002-05-19 01:59:48 +00:00
for ( int i = 0 ; i < MAX_TAP_NOTE_ROWS ; i + + )
2002-04-16 17:31:00 +00:00
{
2002-08-23 20:18:29 +00:00
if ( i % ROWS_PER_BEAT ! = 0 )
2002-04-16 17:31:00 +00:00
{
// filter out all non-quarter notes
for ( int c = 0 ; c < m_iNumTracks ; c + + )
{
m_TapNotes [ c ] [ i ] = ' 0 ' ;
}
}
}
// leave HoldNotes unchanged (what should be do with them?)
}
void NoteData : : Convert2sAnd3sToHoldNotes ( )
{
2002-07-11 19:02:26 +00:00
// Any note will end a hold (not just a '3'). This makes parsing DWIs much easier,
// plus we don't want tap notes in the middle of a hold!
2002-04-16 17:31:00 +00:00
for ( int col = 0 ; col < m_iNumTracks ; col + + ) // foreach column
{
2002-05-19 01:59:48 +00:00
for ( int i = 0 ; i < MAX_TAP_NOTE_ROWS ; i + + ) // foreach TapNote element
2002-04-16 17:31:00 +00:00
{
if ( m_TapNotes [ col ] [ i ] = = ' 2 ' ) // this is a HoldNote begin marker
{
2002-06-27 17:49:10 +00:00
for ( int j = i + 1 ; j < MAX_TAP_NOTE_ROWS ; j + + ) // search for end of HoldNote
2002-04-16 17:31:00 +00:00
{
2002-06-29 11:59:09 +00:00
if ( m_TapNotes [ col ] [ j ] ! = ' 0 ' ) // end hold on the next note we see
2002-04-16 17:31:00 +00:00
{
2002-08-13 23:26:46 +00:00
HoldNote hn = { col , NoteRowToBeat ( i ) , NoteRowToBeat ( j ) } ;
2002-04-16 17:31:00 +00:00
AddHoldNote ( hn ) ;
break ;
}
}
}
}
}
}
void NoteData : : ConvertHoldNotesTo2sAnd3s ( )
{
// copy HoldNotes into the new structure, but expand them into 2s and 3s
for ( int i = 0 ; i < m_iNumHoldNotes ; i + + )
{
HoldNote & hn = m_HoldNotes [ i ] ;
2002-08-13 23:26:46 +00:00
int iHoldStartIndex = BeatToNoteRow ( hn . m_fStartBeat ) ;
int iHoldEndIndex = BeatToNoteRow ( hn . m_fEndBeat ) ;
m_TapNotes [ hn . m_iTrack ] [ iHoldStartIndex ] = ' 2 ' ;
m_TapNotes [ hn . m_iTrack ] [ iHoldEndIndex ] = ' 3 ' ;
2002-04-16 17:31:00 +00:00
}
m_iNumHoldNotes = 0 ;
}
void NoteData : : SnapToNearestNoteType ( NoteType nt1 , NoteType nt2 , float fBeginBeat , float fEndBeat )
{
// nt2 is optional and should be -1 if it is not used
float fSnapInterval1 , fSnapInterval2 ;
switch ( nt1 )
{
2002-08-23 20:18:29 +00:00
case NOTE_TYPE_4TH : fSnapInterval1 = 1 / 1.0f ; break ;
case NOTE_TYPE_8TH : fSnapInterval1 = 1 / 2.0f ; break ;
case NOTE_TYPE_12TH : fSnapInterval1 = 1 / 3.0f ; break ;
case NOTE_TYPE_16TH : fSnapInterval1 = 1 / 4.0f ; break ;
2002-04-16 17:31:00 +00:00
default : ASSERT ( false ) ;
}
switch ( nt2 )
{
2002-08-23 20:18:29 +00:00
case NOTE_TYPE_4TH : fSnapInterval2 = 1 / 1.0f ; break ;
case NOTE_TYPE_8TH : fSnapInterval2 = 1 / 2.0f ; break ;
case NOTE_TYPE_12TH : fSnapInterval2 = 1 / 3.0f ; break ;
case NOTE_TYPE_16TH : fSnapInterval2 = 1 / 4.0f ; break ;
case - 1 : fSnapInterval2 = 10000 ; break ; // nothing will ever snap to this. That's what we want!
2002-04-16 17:31:00 +00:00
default : ASSERT ( false ) ;
}
2002-05-19 01:59:48 +00:00
int iNoteIndexBegin = BeatToNoteRow ( fBeginBeat ) ;
int iNoteIndexEnd = BeatToNoteRow ( fEndBeat ) ;
2002-04-16 17:31:00 +00:00
//ConvertHoldNotesTo2sAnd3s();
// iterate over all TapNotes in the interval and snap them
for ( int i = iNoteIndexBegin ; i < = iNoteIndexEnd ; i + + )
{
int iOldIndex = i ;
2002-05-19 01:59:48 +00:00
float fOldBeat = NoteRowToBeat ( iOldIndex ) ;
2002-04-16 17:31:00 +00:00
float fNewBeat1 = froundf ( fOldBeat , fSnapInterval1 ) ;
float fNewBeat2 = froundf ( fOldBeat , fSnapInterval2 ) ;
bool bNewBeat1IsCloser = fabsf ( fNewBeat1 - fOldBeat ) < fabsf ( fNewBeat2 - fOldBeat ) ;
float fNewBeat = bNewBeat1IsCloser ? fNewBeat1 : fNewBeat2 ;
2002-05-19 01:59:48 +00:00
int iNewIndex = BeatToNoteRow ( fNewBeat ) ;
2002-04-16 17:31:00 +00:00
for ( int c = 0 ; c < m_iNumTracks ; c + + )
{
TapNote note = m_TapNotes [ c ] [ iOldIndex ] ;
m_TapNotes [ c ] [ iOldIndex ] = ' 0 ' ;
m_TapNotes [ c ] [ iNewIndex ] = max ( m_TapNotes [ c ] [ iNewIndex ] , note ) ; // this causes HoldNotes to override TapNotes
}
}
//Convert2sAnd3sToHoldNotes();
}
2002-05-19 01:59:48 +00:00
float NoteData : : GetStreamRadarValue ( float fSongSeconds )
{
// density of steps
int iNumNotes = GetNumTapNotes ( ) + GetNumHoldNotes ( ) ;
2002-06-24 22:04:31 +00:00
float fNotesPerSecond = iNumNotes / fSongSeconds ;
2002-07-03 21:27:26 +00:00
float fReturn = fNotesPerSecond / 7 ;
2002-07-03 03:13:13 +00:00
return min ( fReturn , 1.0f ) ;
2002-05-19 01:59:48 +00:00
}
float NoteData : : GetVoltageRadarValue ( float fSongSeconds )
{
2002-07-03 21:27:26 +00:00
float fAvgBPS = GetLastBeat ( ) / fSongSeconds ;
2002-05-19 01:59:48 +00:00
2002-07-03 21:27:26 +00:00
// peak density of steps
float fMaxDensitySoFar = 0 ;
2002-05-19 01:59:48 +00:00
2002-07-03 21:27:26 +00:00
const int BEAT_WINDOW = 8 ;
2002-05-19 01:59:48 +00:00
2002-07-03 21:27:26 +00:00
for ( int i = 0 ; i < MAX_BEATS ; i + = BEAT_WINDOW )
{
int iNumNotesThisWindow = GetNumTapNotes ( ( float ) i , ( float ) i + BEAT_WINDOW ) + GetNumHoldNotes ( ( float ) i , ( float ) i + BEAT_WINDOW ) ;
float fDensityThisWindow = iNumNotesThisWindow / ( float ) BEAT_WINDOW ;
fMaxDensitySoFar = max ( fMaxDensitySoFar , fDensityThisWindow ) ;
2002-05-19 01:59:48 +00:00
}
2002-07-03 21:27:26 +00:00
float fReturn = fMaxDensitySoFar * fAvgBPS / 10 ;
2002-07-03 03:13:13 +00:00
return min ( fReturn , 1.0f ) ;
2002-05-19 01:59:48 +00:00
}
float NoteData : : GetAirRadarValue ( float fSongSeconds )
{
// number of doubles
int iNumDoubles = GetNumDoubles ( ) ;
2002-07-03 03:13:13 +00:00
float fReturn = iNumDoubles / fSongSeconds ;
return min ( fReturn , 1.0f ) ;
2002-05-19 01:59:48 +00:00
}
float NoteData : : GetChaosRadarValue ( float fSongSeconds )
{
2002-07-28 20:28:37 +00:00
// count number of triplets or 16ths
2002-07-03 21:27:26 +00:00
int iNumChaosNotes = 0 ;
2002-07-03 03:13:13 +00:00
for ( int r = 0 ; r < MAX_TAP_NOTE_ROWS ; r + + )
{
2002-08-23 20:18:29 +00:00
if ( ! IsRowEmpty ( r ) & & GetNoteType ( r ) > = NOTE_TYPE_12TH )
2002-07-31 19:40:40 +00:00
iNumChaosNotes + + ;
2002-07-03 03:13:13 +00:00
}
2002-07-04 21:05:18 +00:00
float fReturn = iNumChaosNotes / fSongSeconds * 0.5f ;
2002-07-03 03:13:13 +00:00
return min ( fReturn , 1.0f ) ;
2002-05-19 01:59:48 +00:00
}
float NoteData : : GetFreezeRadarValue ( float fSongSeconds )
{
// number of hold steps
2002-07-03 03:13:13 +00:00
float fReturn = GetNumHoldNotes ( ) / fSongSeconds ;
return min ( fReturn , 1.0f ) ;
2002-05-19 01:59:48 +00:00
}
2002-08-22 22:59:19 +00:00
void NoteData : : LoadTransformed ( const NoteData * pOriginal , int iNewNumTracks , const int iNewToOriginalTrack [ ] )
2002-04-16 17:31:00 +00:00
{
// init
2002-07-03 21:27:26 +00:00
Init ( ) ;
2002-04-16 17:31:00 +00:00
m_iNumTracks = iNewNumTracks ;
// copy tracks
for ( int t = 0 ; t < m_iNumTracks ; t + + )
{
int iOriginalTrack = iNewToOriginalTrack [ t ] ;
int i ;
2002-07-03 21:27:26 +00:00
memcpy ( m_TapNotes [ t ] , pOriginal - > m_TapNotes [ iOriginalTrack ] , MAX_TAP_NOTE_ROWS * sizeof ( m_TapNotes [ 0 ] [ 0 ] ) ) ;
2002-04-16 17:31:00 +00:00
2002-07-03 21:27:26 +00:00
for ( i = 0 ; i < pOriginal - > m_iNumHoldNotes ; i + + )
2002-04-16 17:31:00 +00:00
{
2002-08-22 22:59:19 +00:00
HoldNote hn = pOriginal - > m_HoldNotes [ i ] ;
2002-04-16 17:31:00 +00:00
if ( hn . m_iTrack = = iOriginalTrack )
{
hn . m_iTrack = t ;
this - > AddHoldNote ( hn ) ;
}
}
}
}
2002-06-24 22:04:31 +00:00
2002-08-23 20:18:29 +00:00
NoteType NoteData : : GetSmallestNoteTypeForMeasure ( int iMeasureIndex )
{
const int iMeasureStartIndex = iMeasureIndex * ROWS_PER_MEASURE ;
const int iMeasureLastIndex = ( iMeasureIndex + 1 ) * ROWS_PER_MEASURE - 1 ;
// probe to find the smallest note type
NoteType nt ;
for ( nt = ( NoteType ) 0 ; nt < NUM_NOTE_TYPES ; nt = NoteType ( nt + 1 ) ) // for each NoteType, largest to largest
{
float fBeatSpacing = NoteTypeToBeat ( nt ) ;
int iRowSpacing = roundf ( fBeatSpacing * ROWS_PER_BEAT ) ;
bool bFoundSmallerNote = false ;
for ( int i = iMeasureStartIndex ; i < = iMeasureLastIndex ; i + + ) // for each index in this measure
{
if ( i % iRowSpacing = = 0 )
continue ; // skip
if ( ! IsRowEmpty ( i ) )
{
bFoundSmallerNote = true ;
break ;
}
}
if ( bFoundSmallerNote )
continue ; // searching the next NoteType
else
break ; // stop searching. We found the smallest NoteType
}
if ( nt = = NUM_NOTE_TYPES ) // we didn't find one
return NOTE_TYPE_INVALID ; // well-formed notes created in the editor should never get here
else
return nt ;
}