diff --git a/src/TimingData.cpp b/src/TimingData.cpp index a2744b4196..550be18b6c 100644 --- a/src/TimingData.cpp +++ b/src/TimingData.cpp @@ -47,6 +47,11 @@ void TimingData::AddWarpSegment( const WarpSegment &seg ) m_WarpSegments.insert( upper_bound(m_WarpSegments.begin(), m_WarpSegments.end(), seg), seg ); } +void TimingData::AddTickcountSegment( const TickcountSegment &seg ) +{ + m_TickcountSegments.insert( upper_bound(m_TickcountSegments.begin(), m_TickcountSegments.end(), seg), seg ); +} + /* Change an existing BPM segment, merge identical segments together or insert a new one. */ void TimingData::SetBPMAtRow( int iNoteRow, float fBPM ) { @@ -84,7 +89,7 @@ void TimingData::SetStopAtRow( int iRow, float fSeconds, bool bDelay ) if( m_StopSegments[i].m_iStartRow == iRow ) break; - if( i == m_StopSegments.size() ) // there is no BPMSegment at the current beat + if( i == m_StopSegments.size() ) // there is no StopSegment at the current beat { // create a new StopSegment if( fSeconds > 0 || PREFSMAN->m_bQuirksMode ) @@ -158,6 +163,29 @@ void TimingData::SetWarpAtRow( int iRowAt, float fLengthBeats ) } */ +/* Change an existing Tickcount segment, merge identical segments together or insert a new one. */ +void TimingData::SetTickcountAtRow( int iRow, int iTicks ) +{ + unsigned i; + for( i=0; i= iRow ) + break; + + if( i == m_TickcountSegments.size() || m_TickcountSegments[i].m_iStartRow != iRow ) + { + // No TickcountSegment here. Make a new segment if required. + if( i == 0 || m_TickcountSegments[i-1].m_iTicks != iTicks ) + AddTickcountSegment( TickcountSegment(iRow, iTicks ) ); + } + else // TickcountSegment being modified is m_TickcountSegments[i] + { + if( i > 0 && m_TickcountSegments[i-1].m_iTicks == iTicks ) + m_TickcountSegments.erase( m_TickcountSegments.begin()+i, m_TickcountSegments.begin()+i+1 ); + else + m_TickcountSegments[i].m_iTicks = iTicks; + } +} + float TimingData::GetStopAtRow( int iNoteRow, bool &bDelayOut ) const { bDelayOut = false; // not a delay by default @@ -265,6 +293,30 @@ BPMSegment& TimingData::GetBPMSegmentAtBeat( float fBeat ) return m_BPMSegments[i]; } +int TimingData::GetTickcountSegmentIndexAtRow( int iRow ) +{ + int i; + for (i=0; i < (int)(m_TickcountSegments.size()) - 1; i++ ) + if( m_TickcountSegments[i+1].m_iStartRow > iRow ) + break; + return i; +} + +TickcountSegment& TimingData::GetTickcountSegmentAtRow( int iRow ) +{ + static TickcountSegment empty; + if( m_TickcountSegments.empty() ) + return empty; + + int i = GetTickcountSegmentIndexAtBeat( iRow ); + return m_TickcountSegments[i]; +} + +int TimingData::GetTickcountAtRow( int iRow ) +{ + return m_TickcountSegments[GetTickcountSegmentIndexAtRow( iRow )].m_iTicks; +} + void TimingData::GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpLengthOut ) const { fElapsedTime += PREFSMAN->m_fGlobalOffsetSeconds; diff --git a/src/TimingData.h b/src/TimingData.h index 7aab032a2c..6c4120772c 100644 --- a/src/TimingData.h +++ b/src/TimingData.h @@ -162,6 +162,37 @@ struct WarpSegment } }; +/* + * A tickcount segment is used to better replicate the checkpoint hold + * system used by various based video games. The number is used to + * represent how many ticks can be counted in one beat. + */ +struct TickcountSegment +{ + TickcountSegment() : m_iStartRow(-1), m_iTicks(2) { } + TickcountSegment( int s, int t ){ m_iStartRow = max( 0, s ); m_iTicks = max( 1, t ); } + int m_iStartRow; + int m_iTicks; + + bool operator==( const TickcountSegment &other ) const + { + COMPARE( m_iStartRow ); + COMPARE( m_iTicks ); + return true; + } + bool operator!=( const TickcountSegment &other ) const { return !operator==(other); } + bool operator<( const TickcountSegment &other ) const { return m_iStartRow < other.m_iStartRow; } + bool operator<=( const TickcountSegment &other ) const + { + return ( operator<(other) || operator==(other) ); + } + bool operator>( const TickcountSegment &other ) const { return m_iStartRow > other.m_iStartRow; } + bool operator>=( const TickcountSegment &other ) const + { + return ( operator>(other) || operator==(other) ); + } +}; + class TimingData { public: @@ -184,14 +215,23 @@ public: void SetTimeSignatureDenominatorAtBeat( float fBeat, int iDenominator ) { SetTimeSignatureDenominatorAtRow( BeatToNoteRow(fBeat), iDenominator); } int GetWarpToRow( int iWarpBeginRow ) const; void SetDelayAtRow( int iNoteRow, float fSeconds ); // sm-ssc + void SetTickcountAtRow( int iRow, int iTicks ); + void SetTickcountAtBeat( float fBeat, int iTicks ) { SetTickcountAtRow( BeatToNoteRow( fBeat ), iTicks ); } + int GetTickcountAtRow( int iRow ); + int GetTickcountAtBeat( float fBeat ) { return GetTickcountAtRow( BeatToNoteRow(fBeat) ); } void MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor ); void AddBPMSegment( const BPMSegment &seg ); void AddStopSegment( const StopSegment &seg ); void AddTimeSignatureSegment( const TimeSignatureSegment &seg ); void AddWarpSegment( const WarpSegment &seg ); + void AddTickcountSegment( const TickcountSegment &seg ); int GetBPMSegmentIndexAtBeat( float fBeat ); const TimeSignatureSegment& GetTimeSignatureSegmentAtBeat( float fBeat ) const; BPMSegment& GetBPMSegmentAtBeat( float fBeat ); + int GetTickcountSegmentIndexAtRow( int iRow ); + int GetTickcountSegmentIndexAtBeat( float fBeat ) { return GetTickcountSegmentIndexAtRow( BeatToNoteRow(fBeat) ); } + TickcountSegment& GetTickcountSegmentAtRow( int iRow ); + TickcountSegment& GetTickcountSegmentAtBeat( float fBeat ) { return GetTickcountSegmentAtRow( BeatToNoteRow(fBeat) ); } void NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const; void GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpLengthOut ) const; @@ -231,6 +271,12 @@ public: COMPARE( m_WarpSegments.size() ); for( unsigned i=0; i m_BPMSegments; // this must be sorted before gameplay - vector m_StopSegments; // this must be sorted before gameplay - vector m_vTimeSignatureSegments; // this must be sorted before gameplay - vector m_WarpSegments; // this must be sorted before gameplay + // All of the following vectors must be sorted before gameplay. + vector m_BPMSegments; + vector m_StopSegments; + vector m_vTimeSignatureSegments; + vector m_WarpSegments; + vector m_TickcountSegments; float m_fBeat0OffsetInSeconds; bool m_bHasNegativeBpms; // only used for Lua bindings in Song (to be moved to TimingData later) };