From 9ffe6166e2927df4065584d1141003079892ae57 Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Sun, 15 May 2011 13:48:10 -0400 Subject: [PATCH 1/2] [splittiming] Implement, add lua hook. --- Docs/Luadoc/Lua.xml | 1 + Docs/Luadoc/LuaDocumentation.xml | 3 + src/TimingData.cpp | 124 ++++++++++++++++++++++++++++++- src/TimingData.h | 99 ++++++++++++++++++++++++ 4 files changed, 226 insertions(+), 1 deletion(-) diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index f41bb848cb..966502afbc 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -1405,6 +1405,7 @@ + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index 39df9f4188..24492431aa 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -3516,6 +3516,9 @@ Returns true if the TimingData contains warps. + + Returns true if the TimingData contains speed scrolling changes. + diff --git a/src/TimingData.cpp b/src/TimingData.cpp index 2f93424bc7..45302128b8 100644 --- a/src/TimingData.cpp +++ b/src/TimingData.cpp @@ -68,6 +68,11 @@ void TimingData::AddLabelSegment( const LabelSegment &seg ) m_LabelSegments.insert( upper_bound(m_LabelSegments.begin(), m_LabelSegments.end(), seg), seg ); } +void TimingData::AddSpeedSegment( const SpeedSegment &seg ) +{ + m_SpeedSegments.insert( upper_bound(m_SpeedSegments.begin(), m_SpeedSegments.end(), seg), seg ); +} + /* Change an existing BPM segment, merge identical segments together or insert a new one. */ void TimingData::SetBPMAtRow( int iNoteRow, float fBPM ) { @@ -250,6 +255,47 @@ void TimingData::SetLabelAtRow( int iRow, const RString sLabel ) } } +void TimingData::SetSpeedAtRow( int iRow, float fPercent, float fWait ) +{ + unsigned i; + for( i = 0; i < m_SpeedSegments.size(); i++ ) + { + if( m_SpeedSegments[i].m_iStartRow >= iRow) + break; + } + + if ( i == m_SpeedSegments.size() || m_SpeedSegments[i].m_iStartRow != iRow ) + { + if( i == 0 || + ( m_SpeedSegments[i-1].m_fPercent != fPercent + || m_SpeedSegments[i-1].m_fWait != fWait ) ) + AddSpeedSegment( SpeedSegment(iRow, fPercent, fWait) ); + } + else + { + if( i > 0 && m_SpeedSegments[i-1].m_fPercent == fPercent + && m_SpeedSegments[i-1].m_fWait == fWait ) + m_SpeedSegments.erase( m_SpeedSegments.begin()+i, + m_SpeedSegments.begin()+i+1 ); + else + { + m_SpeedSegments[i].m_fPercent = fPercent; + m_SpeedSegments[i].m_fWait = fWait; + } + } +} + +void TimingData::SetSpeedPercentAtRow( int iRow, float fPercent ) +{ + SetSpeedAtRow( iRow, fPercent, GetSpeedSegmentAtBeat( NoteRowToBeat( iRow ) ).m_fWait ); +} + +void TimingData::SetSpeedWaitAtRow( int iRow, float fWait ) +{ + SetSpeedAtRow( iRow, GetSpeedSegmentAtBeat( NoteRowToBeat( iRow ) ).m_fPercent, fWait ); +} + + float TimingData::GetStopAtRow( int iNoteRow, bool bDelay ) const { for( unsigned i=0; i(i); } +int TimingData::GetSpeedSegmentIndexAtRow( int iRow ) const +{ + unsigned i; + for (i=0; i < m_SpeedSegments.size() - 1; i++ ) + if( m_SpeedSegments[i+1].m_iStartRow > iRow ) + break; + return static_cast(i); +} + BPMSegment& TimingData::GetBPMSegmentAtRow( int iNoteRow ) { static BPMSegment empty; @@ -451,6 +516,15 @@ TimeSignatureSegment& TimingData::GetTimeSignatureSegmentAtRow( int iRow ) return m_vTimeSignatureSegments[i]; } +SpeedSegment& TimingData::GetSpeedSegmentAtRow( int iRow ) +{ + unsigned i; + for( i=0; i iRow ) + break; + return m_SpeedSegments[i]; +} + int TimingData::GetTimeSignatureNumeratorAtRow( int iRow ) { return GetTimeSignatureSegmentAtRow( iRow ).m_iNumerator; @@ -845,6 +919,17 @@ void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool m_LabelSegments[i].m_iStartRow = lrintf( (iSegStart - iStartIndex) * fScale ) + iStartIndex; } + for ( unsigned i = 0; i < m_SpeedSegments.size(); i++ ) + { + const int iSegStart = m_SpeedSegments[i].m_iStartRow; + if( iSegStart < iStartIndex ) + continue; + else if( iSegStart > iEndIndex ) + m_SpeedSegments[i].m_iStartRow += lrintf( (iEndIndex - iStartIndex) * (fScale - 1) ); + else + m_SpeedSegments[i].m_iStartRow = lrintf( (iSegStart - iStartIndex) * fScale ) + iStartIndex; + } + // adjust BPM changes to preserve timing if( bAdjustBPM ) { @@ -929,6 +1014,14 @@ void TimingData::InsertRows( int iStartRow, int iRowsToAdd ) continue; labl.m_iStartRow += iRowsToAdd; } + + for( unsigned i = 0; i < m_SpeedSegments.size(); i++ ) + { + SpeedSegment &sped = m_SpeedSegments[i]; + if( sped.m_iStartRow < iStartRow ) + continue; + sped.m_iStartRow += iRowsToAdd; + } if( iStartRow == 0 ) { @@ -1086,6 +1179,22 @@ void TimingData::DeleteRows( int iStartRow, int iRowsToDelete ) } labl.m_iStartRow -= iRowsToDelete; } + + for( unsigned i = 0; i < m_SpeedSegments.size(); i++ ) + { + SpeedSegment &sped = m_SpeedSegments[i]; + + if( sped.m_iStartRow < iStartRow ) + continue; + + if( sped.m_iStartRow < iStartRow+iRowsToDelete ) + { + m_SpeedSegments.erase( m_SpeedSegments.begin()+i, m_SpeedSegments.begin()+i+1 ); + --i; + continue; + } + sped.m_iStartRow -= iRowsToDelete; + } this->SetBPMAtRow( iStartRow, fNewBPM ); } @@ -1133,7 +1242,13 @@ void TimingData::TidyUpData() LabelSegment seg(0, "Song Start"); m_LabelSegments.push_back( seg ); } - + + // Always be sure there is a starting speed. + if( m_SpeedSegments.empty() ) + { + SpeedSegment seg(0, 1, 0); + m_SpeedSegments.push_back( seg ); + } } @@ -1155,6 +1270,11 @@ bool TimingData::HasWarps() const return m_WarpSegments.size()>0; } +bool TimingData::HasSpeedChanges() const +{ + return m_SpeedSegments.size()>1; +} + void TimingData::NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const { iMeasureIndexOut = 0; @@ -1201,6 +1321,7 @@ public: static int HasStops( T* p, lua_State *L ) { lua_pushboolean(L, p->HasStops()); return 1; } static int HasBPMChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasBpmChanges()); return 1; } static int HasWarps( T* p, lua_State *L ) { lua_pushboolean(L, p->HasWarps()); return 1; } + static int HasSpeedChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasSpeedChanges()); return 1; } static int GetStops( T* p, lua_State *L ) { vector vStops; @@ -1288,6 +1409,7 @@ public: ADD_METHOD( HasStops ); ADD_METHOD( HasBPMChanges ); ADD_METHOD( HasWarps ); + ADD_METHOD( HasSpeedChanges ); ADD_METHOD( GetStops ); ADD_METHOD( GetDelays ); ADD_METHOD( GetBPMs ); diff --git a/src/TimingData.h b/src/TimingData.h index c1d1b2ea87..d68e2ec98e 100644 --- a/src/TimingData.h +++ b/src/TimingData.h @@ -1339,6 +1339,101 @@ public: */ float GetNextLabelSegmentBeatAtBeat( float fBeat ) const { return GetNextLabelSegmentBeatAtRow( BeatToNoteRow(fBeat) ); } + + /** + * @brief Retrieve the Speed's percent at the given row. + * @param iNoteRow the row in question. + * @return the percent. + */ + float GetSpeedPercentAtRow( int iNoteRow ); + /** + * @brief Retrieve the Speed's percent at the given beat. + * @param fBeat the beat in question. + * @return the percent. + */ + float GetSpeedPercentAtBeat( float fBeat ) { return GetSpeedPercentAtRow( BeatToNoteRow(fBeat) ); } + /** + * @brief Retrieve the Speed's wait at the given row. + * @param iNoteRow the row in question. + * @return the wait. + */ + float GetSpeedWaitAtRow( int iNoteRow ); + /** + * @brief Retrieve the Speed's wait at the given beat. + * @param fBeat the beat in question. + * @return the wait. + */ + float GetSpeedWaitAtBeat( float fBeat ) { return GetSpeedWaitAtRow( BeatToNoteRow(fBeat) ); } + /** + * @brief Set the row to have the new Speed. + * @param iNoteRow the row to have the new Speed. + * @param fPercent the percent. + * @param fWait the wait. + */ + void SetSpeedAtRow( int iNoteRow, float fPercent, float fWait ); + /** + * @brief Set the beat to have the new Speed. + * @param fBeat the beat to have the new Speed. + * @param fPercent the percent. + * @param fWait the wait. + */ + void SetSpeedAtBeat( float fBeat, float fPercent, float fWait ) { SetSpeedAtRow( BeatToNoteRow(fBeat), fPercent, fWait ); } + /** + * @brief Set the row to have the new Speed percent. + * @param iNoteRow the row to have the new Speed percent. + * @param fPercent the percent. + */ + void SetSpeedPercentAtRow( int iNoteRow, float fPercent ); + /** + * @brief Set the beat to have the new Speed percent. + * @param fBeat the beat to have the new Speed percent. + * @param fPercent the percent. + */ + void SetSpeedPercentAtBeat( float fBeat, float fPercent ) { SetSpeedPercentAtRow( BeatToNoteRow(fBeat), fPercent); } + /** + * @brief Set the row to have the new Speed wait. + * @param iNoteRow the row to have the new Speed wait. + * @param fWait the wait. + */ + void SetSpeedWaitAtRow( int iNoteRow, float fWait ); + /** + * @brief Set the beat to have the new Speed wait. + * @param fBeat the beat to have the new Speed wait. + * @param fWait the wait. + */ + void SetSpeedWaitAtBeat( float fBeat, float fWait ) { SetSpeedWaitAtRow( BeatToNoteRow(fBeat), fWait); } + /** + * @brief Retrieve the SpeedSegment at the specified row. + * @param iNoteRow the row that has a SpeedSegment. + * @return the SpeedSegment in question. + */ + SpeedSegment& GetSpeedSegmentAtRow( int iNoteRow ); + /** + * @brief Retrieve the SpeedSegment at the specified beat. + * @param fBeat the beat that has a SpeedSegment. + * @return the SpeedSegment in question. + */ + SpeedSegment& GetSpeedSegmentAtBeat( float fBeat ) { return GetSpeedSegmentAtRow( BeatToNoteRow(fBeat) ); } + /** + * @brief Retrieve the index of the SpeedSegments at the specified row. + * @param iNoteRow the row that has a SpeedSegment. + * @return the SpeedSegment's index in question. + */ + int GetSpeedSegmentIndexAtRow( int iNoteRow ) const; + /** + * @brief Retrieve the index of the SpeedSegments at the specified beat. + * @param fBeat the beat that has a SpeedSegment. + * @return the SpeedSegment's index in question. + */ + int GetSpeedSegmentIndexAtBeat( float fBeat ) const { return GetSpeedSegmentIndexAtRow( BeatToNoteRow(fBeat) ); } + /** + * @brief Add the SpeedSegment to the TimingData. + * @param seg the new SpeedSegment. + */ + void AddSpeedSegment( const SpeedSegment &seg ); + + + void MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor ); void NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const; @@ -1381,6 +1476,10 @@ public: * @return true if there is at least one warp, false otherwise. */ bool HasWarps() const; + /** + * @brief View the TimingData to see if a song changes its speed scrolling at any point. + * @return true if there is at least one change, false otherwise. */ + bool HasSpeedChanges() const; /** * @brief Compare two sets of timing data to see if they are equal. * @param other the other TimingData. From 640f8fe9fc3c9133c18db2afc67065877f93d3e2 Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Sun, 15 May 2011 14:05:02 -0400 Subject: [PATCH 2/2] [splittiming] PlayerOptions -> HasTimingChanges. Steps gained most of the functionality and lua doc. Leaving song version in: may still have a use thanks to #DISPLAYBPM. --- Docs/Luadoc/Lua.xml | 1 + Docs/Luadoc/LuaDocumentation.xml | 3 +++ src/PlayerOptions.cpp | 2 +- src/Steps.cpp | 18 ++++++++++++++++++ src/Steps.h | 5 +++++ 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index 966502afbc..dfb4c7cce3 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -1349,6 +1349,7 @@ + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index 24492431aa..df9f17081b 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -3368,6 +3368,9 @@ Returns the numerical difficulty of the Steps. + + Returns true if the song has significant timing changes. + Returns the complete list of RadarValues for player pn. Use to grab a specific value. diff --git a/src/PlayerOptions.cpp b/src/PlayerOptions.cpp index 036f1d314c..cbfb031bbf 100644 --- a/src/PlayerOptions.cpp +++ b/src/PlayerOptions.cpp @@ -680,7 +680,7 @@ bool PlayerOptions::operator==( const PlayerOptions &other ) const bool PlayerOptions::IsEasierForSongAndSteps( Song* pSong, Steps* pSteps, PlayerNumber pn ) const { - if( m_fTimeSpacing && pSong->HasSignificantBpmChangesOrStops() ) + if( m_fTimeSpacing && pSteps->HasSignificantTimingChanges() ) return true; const RadarValues &rv = pSteps->GetRadarValues( pn ); if( m_bTransforms[TRANSFORM_NOHOLDS] && rv[RadarCategory_Holds]>0 ) diff --git a/src/Steps.cpp b/src/Steps.cpp index 5c2c91f063..3174e011b2 100644 --- a/src/Steps.cpp +++ b/src/Steps.cpp @@ -402,6 +402,21 @@ void Steps::SetMeter( int meter ) m_iMeter = meter; } +bool Steps::HasSignificantTimingChanges() const +{ + if( m_Timing.HasStops() ) + return true; + + /* TODO: Deal with DisplayBPM here...if possible? + * Song's version may still be useful. */ + + else if( m_Timing.HasBpmChanges() || m_Timing.HasWarps() || m_Timing.HasSpeedChanges() ) + { + return true; + } + return false; +} + void Steps::SetCachedRadarValues( const RadarValues v[NUM_PLAYERS] ) { DeAutogen(); @@ -426,6 +441,8 @@ public: DEFINE_METHOD( IsAnEdit, IsAnEdit() ) DEFINE_METHOD( IsAPlayerEdit, IsAPlayerEdit() ) + static int HasSignificantTimingChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasSignificantTimingChanges()); return 1; } + static int GetRadarValues( T* p, lua_State *L ) { PlayerNumber pn = Enum::Check(L, 1); @@ -454,6 +471,7 @@ public: ADD_METHOD( GetFilename ); ADD_METHOD( GetHash ); ADD_METHOD( GetMeter ); + ADD_METHOD( HasSignificantTimingChanges ); ADD_METHOD( GetRadarValues ); //ADD_METHOD( GetSMNoteData ); ADD_METHOD( GetStepsType ); diff --git a/src/Steps.h b/src/Steps.h index e796f99847..078295bc71 100644 --- a/src/Steps.h +++ b/src/Steps.h @@ -120,6 +120,11 @@ public: /** @brief Timing data */ TimingData m_Timing; + + /** + * @brief Determine if the Steps have any major timing changes during gameplay. + * @return true if it does, or false otherwise. */ + bool HasSignificantTimingChanges() const; // Lua void PushSelf( lua_State *L );