int NoteData::GetFirstRow() const { int iEarliestRowFoundSoFar = -1; int i; for( i=0; i < int(m_TapNotes[0].size()); i++ ) { if( !IsRowEmpty(i) ) { iEarliestRowFoundSoFar = i; break; } } for( i=0; i<GetNumHoldNotes(); i++ ) { if( iEarliestRowFoundSoFar == -1 || GetHoldNote(i).iStartRow < iEarliestRowFoundSoFar ) iEarliestRowFoundSoFar = GetHoldNote(i).iStartRow; } if( iEarliestRowFoundSoFar == -1 ) // there are no notes return 0; return iEarliestRowFoundSoFar; }
int NoteData::RowNeedsHands( const int row ) const { int iNumNotesThisIndex = 0; for( int t=0; t<m_iNumTracks; t++ ) { TapNote tn = GetTapNoteX(t, row); switch( tn.type ) { case TapNote::mine: case TapNote::empty: case TapNote::hold_tail: continue; // skip these types - they don't count } ++iNumNotesThisIndex; } /* We must have at least one non-hold-body at this row to count it. */ if( !iNumNotesThisIndex ) return false; if( iNumNotesThisIndex < 3 ) { /* We have at least one, but not enough. Count holds. */ for( int j=0; j<GetNumHoldNotes(); j++ ) { const HoldNote &hn = GetHoldNote(j); if( hn.iStartRow+1 <= row && row <= hn.iEndRow ) ++iNumNotesThisIndex; } } return iNumNotesThisIndex >= 3; }
/* See NoteData::GetNumHands(). */ int NoteDataWithScoring::GetSuccessfulHands( float fStartBeat, float fEndBeat ) const { if( fEndBeat == -1 ) fEndBeat = GetNumBeats(); int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); /* Clamp to known-good ranges. */ iStartIndex = max( iStartIndex, 0 ); iEndIndex = min( iEndIndex, GetNumRows()-1 ); int iNum = 0; for( int i=iStartIndex; i<=iEndIndex; i++ ) { if( !RowNeedsHands(i) ) continue; bool Missed = false; for( int t=0; t<GetNumTracks(); t++ ) { TapNote tn = GetTapNoteX(t, i); if( tn.type == TapNote::empty ) continue; if( tn.type == TapNote::mine ) // mines don't count continue; if( GetTapNoteScore(t, i) <= TNS_BOO ) Missed = true; } if( Missed ) continue; /* Check hold scores. */ for( int j=0; j<GetNumHoldNotes(); j++ ) { const HoldNote &hn = GetHoldNote(j); HoldNoteResult hnr = GetHoldNoteResult( hn ); /* Check if the row we're checking is in range. */ if( !hn.RowIsInRange(i) ) continue; /* If a hold is released *after* a hands containing it, the hands is * still good. So, ignore the judgement and only examine iLastHeldRow * to be sure that the hold was still held at the point of this row. * (Note that if the hold head tap was missed, then iLastHeldRow == i * and this won't fail--but the tap check above will have already failed.) */ if( hnr.iLastHeldRow < i ) Missed = true; } if( !Missed ) iNum++; } return iNum; }
/* Get a hold note with the same track and end row as hn. */ int NoteData::GetMatchingHoldNote( const HoldNote &hn ) const { for( int i=0; i<GetNumHoldNotes(); i++ ) // for each HoldNote { const HoldNote &ret = GetHoldNote(i); if( ret.iTrack == hn.iTrack && ret.iEndRow == hn.iEndRow ) return i; } FAIL_M( ssprintf("%i..%i, %i", hn.iStartRow, hn.iEndRow, hn.iTrack) ); }
void NoteData::ConvertHoldNotesTo4s() { // copy HoldNotes into the new structure, but expand them into 4s for( int i=0; i<GetNumHoldNotes(); i++ ) { const HoldNote &hn = GetHoldNote(i); for( int j = hn.iStartRow; j < hn.iEndRow; ++j) SetTapNote(hn.iTrack, j, TAP_ORIGINAL_HOLD); } m_HoldNotes.clear(); }
int NoteData::GetLastRow() const { int iOldestRowFoundSoFar = 0; int i; for( i = int(m_TapNotes[0].size()); i>=0; i-- ) // iterate back to front { if( !IsRowEmpty(i) ) { iOldestRowFoundSoFar = i; break; } } for( i=0; i<GetNumHoldNotes(); i++ ) { if( GetHoldNote(i).iEndRow > iOldestRowFoundSoFar ) iOldestRowFoundSoFar = GetHoldNote(i).iEndRow; } return iOldestRowFoundSoFar; }
void NoteData::RemoveHoldNote( int iHoldIndex ) { ASSERT( iHoldIndex >= 0 && iHoldIndex < GetNumHoldNotes() ); HoldNote& hn = GetHoldNote(iHoldIndex); const int iHoldStartIndex = hn.iStartRow; // delete a tap note at the start of this hold SetTapNote(hn.iTrack, iHoldStartIndex, TAP_EMPTY); // remove from list m_HoldNotes.erase(m_HoldNotes.begin()+iHoldIndex, m_HoldNotes.begin()+iHoldIndex+1); }
/* "102000301" == * "104444001" */ void NoteData::ConvertHoldNotesTo2sAnd3s() { // copy HoldNotes into the new structure, but expand them into 2s and 3s for( int i=0; i<GetNumHoldNotes(); i++ ) { const HoldNote &hn = GetHoldNote(i); /* If they're the same, then they got clamped together, so just ignore it. */ if( hn.iStartRow != hn.iEndRow ) { SetTapNote( hn.iTrack, hn.iStartRow, TAP_ORIGINAL_HOLD_HEAD ); SetTapNote( hn.iTrack, hn.iEndRow, TAP_ORIGINAL_HOLD_TAIL ); } } m_HoldNotes.clear(); }
int NoteData::GetNumHoldNotes( float fStartBeat, float fEndBeat ) const { if( fEndBeat == -1 ) fEndBeat = GetNumBeats(); int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); int iNumHolds = 0; for( int i=0; i<GetNumHoldNotes(); i++ ) { const HoldNote &hn = GetHoldNote(i); if( iStartIndex <= hn.iStartRow && hn.iEndRow <= iEndIndex ) iNumHolds++; } return iNumHolds; }
int NoteDataWithScoring::GetNumHoldNotesWithScore( HoldNoteScore hns, const float fStartBeat, float fEndBeat ) const { int iNumSuccessfulHolds = 0; if(fEndBeat == -1) fEndBeat = GetNumBeats(); int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); for( int i=0; i<GetNumHoldNotes(); i++ ) { const HoldNote &hn = GetHoldNote(i); if( iStartIndex > hn.iStartRow || hn.iEndRow > iEndIndex ) continue; if( GetHoldNoteScore(hn) == hns ) iNumSuccessfulHolds++; } return iNumSuccessfulHolds; }
void NoteData::AddHoldNote( HoldNote add ) { ASSERT( add.iStartRow>=0 && add.iEndRow>=0 ); int i; // look for other hold notes that overlap and merge them // XXX: this is done implicitly with 4s, but 4s uses this function. // Rework this later. for( i=0; i<GetNumHoldNotes(); i++ ) // for each HoldNote { HoldNote &other = GetHoldNote(i); if( add.iTrack == other.iTrack && // the tracks correspond add.RangeOverlaps(other) ) // they overlap { add.iStartRow = min(add.iStartRow, other.iStartRow); add.iEndRow = max(add.iEndRow, other.iEndRow); // delete this HoldNote RemoveHoldNote( i ); --i; } } int iAddStartIndex = add.iStartRow; int iAddEndIndex = add.iEndRow; // delete TapNotes under this HoldNote for( i=iAddStartIndex+1; i<=iAddEndIndex; i++ ) SetTapNote( add.iTrack, i, TAP_EMPTY ); // add a tap note at the start of this hold SetTapNote( add.iTrack, iAddStartIndex, TAP_ORIGINAL_HOLD_HEAD ); // Hold begin marker. Don't draw this, but do grade it. m_HoldNotes.push_back(add); }