/* * NoteData is organized by: * track - corresponds to different columns of notes on the screen * row/index - corresponds to subdivisions of beats */ #include "global.h" #include "NoteData.h" #include "RageUtil.h" #include "RageLog.h" NoteData::NoteData() { Init(); } void NoteData::Init() { ClearAll(); m_TapNotes.clear(); } NoteData::~NoteData() { } int NoteData::GetNumTracks() const { return m_TapNotes.size(); } void NoteData::SetNumTracks( int iNewNumTracks ) { ASSERT( iNewNumTracks > 0 ); m_TapNotes.resize( iNewNumTracks ); /* Remove all hold notes that are out of bounds. */ // Iterate backwards so that we can delete. for( int h = m_HoldNotes.size()-1; h >= 0; --h ) if( m_HoldNotes[h].iTrack >= iNewNumTracks ) m_HoldNotes.erase( m_HoldNotes.begin()+h ); } /* Clear [iNoteIndexBegin,iNoteIndexEnd]; that is, including iNoteIndexEnd. */ void NoteData::ClearRange( int iNoteIndexBegin, int iNoteIndexEnd ) { this->ConvertHoldNotesTo4s(); for( int t=0; tConvert4sToHoldNotes(); } void NoteData::ClearAll() { for( int t=0; tFrom4s( To ); } void NoteData::Config( const NoteData& from ) { SetNumTracks( from.GetNumTracks() ); } void NoteData::CopyAll( const NoteData& from ) { Config(from); ClearAll(); for( int c=0; c& addTo ) const { for( int t=0; t=0 && add.iEndRow>=0 ); // 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( int i=0; i= 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); } /* 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 setUsedIndices; for( int t=0; t::iterator iter = m_AttackMap.find( tn.attackIndex ); ASSERT( iter != m_AttackMap.end() ); return iter->second; } int NoteData::GetFirstRow() const { int iEarliestRowFoundSoFar = -1; for( int t=0; t < GetNumTracks(); t++ ) { const TrackMap &trackMap = m_TapNotes[t]; TrackMap::const_iterator iter = trackMap.begin(); if( iter == trackMap.end() ) // trackMap is empty continue; if( iEarliestRowFoundSoFar == -1 ) iEarliestRowFoundSoFar = iter->first; else iEarliestRowFoundSoFar = min( iEarliestRowFoundSoFar, iter->first ); } for( int i=0; ifirst ); } for( int i=0; i iOldestRowFoundSoFar ) iOldestRowFoundSoFar = GetHoldNote(i).iEndRow; } return iOldestRowFoundSoFar; } int NoteData::GetNumTapNotes( float fStartBeat, float fEndBeat ) const { if( fEndBeat == -1 ) fEndBeat = 999999; int iNumNotes = 0; int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); /* Clamp to known-good ranges. */ iStartIndex = max( iStartIndex, 0 ); iEndIndex = min( iEndIndex, GetLastRow() ); for( int t=0; t= 3; } int NoteData::GetNumHands( float fStartBeat, float fEndBeat ) const { /* Count the number of times you have to use your hands. This includes * three taps at the same time, a tap while two hold notes are being held, * etc. Only count rows that have at least one tap note (hold heads count). * Otherwise, every row of hold notes counts, so three simultaneous hold * notes will count as hundreds of "hands". */ if( fEndBeat == -1 ) fEndBeat = GetLastBeat(); int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); /* Clamp to known-good ranges. */ iStartIndex = max( iStartIndex, 0 ); iEndIndex = min( iEndIndex, GetLastRow() ); int iNum = 0; FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( *this, i, iStartIndex, iEndIndex ) { if( !RowNeedsHands(i) ) continue; iNum++; } return iNum; } int NoteData::GetNumN( int MinTaps, float fStartBeat, float fEndBeat ) const { if( fEndBeat == -1 ) fEndBeat = GetLastBeat(); int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); /* Clamp to known-good ranges. */ iStartIndex = max( iStartIndex, 0 ); iEndIndex = min( iEndIndex, GetLastRow() ); int iNum = 0; FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( *this, i, iStartIndex, iEndIndex ) { int iNumNotesThisIndex = 0; for( int t=0; t= MinTaps ) iNum++; } return iNum; } int NoteData::GetNumHoldNotes( float fStartBeat, float fEndBeat ) const { if( fEndBeat == -1 ) fEndBeat = GetLastBeat(); int iStartIndex = BeatToNoteRow( fStartBeat ); int iEndIndex = BeatToNoteRow( fEndBeat ); int iNumHolds = 0; for( int i=0; i= %i (to %i)", iOriginalTrack, Original.GetNumTracks(), iOriginalTrackToTakeFrom[t])); if( iOriginalTrack == -1 ) continue; m_TapNotes[t] = Original.m_TapNotes[iOriginalTrack]; } Convert4sToHoldNotes(); m_AttackMap = Original.GetAttackMap(); } void NoteData::PadTapNotes(int rows) { // Nothing to do for a track map. } void NoteData::MoveTapNoteTrack(int dest, int src) { if(dest == src) return; m_TapNotes[dest] = m_TapNotes[src]; m_TapNotes[src].clear(); } void NoteData::SetTapNote( int track, int row, TapNote t ) { DEBUG_ASSERT( track>=0 && track& addTo ) { for( unsigned i=0; i viTracks; viTracks.clear(); GetTracksHeldAtRow( row, viTracks ); return viTracks.size(); } bool NoteData::GetNextTapNoteRowForTrack( int track, int &rowInOut ) const { const TrackMap &mapTrack = m_TapNotes[track]; // lower_bound and upper_bound have the same effect here because duplicate // keys aren't allowed. TrackMap::const_iterator iter = mapTrack.lower_bound( rowInOut+1 ); // "find the first note for which row+1 < key == false" if( iter != mapTrack.end() ) { ASSERT( iter->first > rowInOut ); rowInOut = iter->first; return true; } else { return false; } } bool NoteData::GetNextTapNoteRowForAllTracks( int &rowInOut ) const { int iClosestNextRow = 999999; bool bAnyHaveNextNote = false; for( int t=0; t