From 60133353c11b33392d7fd5bcbdb9290ac5cb7d35 Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Mon, 16 May 2011 02:00:17 -0400 Subject: [PATCH] [splittiming] Time for FakeSegments. No, these won't replace Fake Notes. --- Docs/Luadoc/Lua.xml | 1 + Docs/Luadoc/LuaDocumentation.xml | 3 + src/TimingData.cpp | 134 +++++++++++++++++++++++ src/TimingData.h | 182 +++++++++++++++++++++++++++++++ 4 files changed, 320 insertions(+) diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index dfb4c7cce3..6aa9eda455 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -1406,6 +1406,7 @@ + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index df9f17081b..1e519bf91c 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -3519,6 +3519,9 @@ Returns true if the TimingData contains warps. + + Returns true if the TimingData contains fake segments. + Returns true if the TimingData contains speed scrolling changes. diff --git a/src/TimingData.cpp b/src/TimingData.cpp index 3fc509fab4..e4f0375e39 100644 --- a/src/TimingData.cpp +++ b/src/TimingData.cpp @@ -73,6 +73,11 @@ void TimingData::AddSpeedSegment( const SpeedSegment &seg ) m_SpeedSegments.insert( upper_bound(m_SpeedSegments.begin(), m_SpeedSegments.end(), seg), seg ); } +void TimingData::AddFakeSegment( const FakeSegment &seg ) +{ + m_FakeSegments.insert( upper_bound(m_FakeSegments.begin(), m_FakeSegments.end(), seg), seg ); +} + /* Change an existing BPM segment, merge identical segments together or insert a new one. */ void TimingData::SetBPMAtRow( int iNoteRow, float fBPM ) { @@ -288,6 +293,31 @@ void TimingData::SetSpeedAtRow( int iRow, float fPercent, float fWait, unsigned } } +void TimingData::SetFakeAtRow( int iRow, float fNew ) +{ + unsigned i; + for( i=0; i 0 && NoteRowToBeat(iRow) < fNew; + if( i == m_FakeSegments.size() ) + { + if( valid ) + { + AddFakeSegment( FakeSegment(iRow, fNew) ); + } + } + else + { + if( valid ) + { + m_FakeSegments[i].m_fEndBeat = fNew; + } + else + m_FakeSegments.erase( m_FakeSegments.begin()+i, m_FakeSegments.begin()+i+1 ); + } +} + void TimingData::SetSpeedPercentAtRow( int iRow, float fPercent ) { SetSpeedAtRow( iRow, @@ -373,6 +403,18 @@ unsigned short TimingData::GetSpeedModeAtRow( int iRow ) return GetSpeedSegmentAtRow( iRow ).m_usMode; } +float TimingData::GetFakeAtRow( int iFakeRow ) const +{ + for( unsigned i=0; i(i); } +int TimingData::GetFakeSegmentIndexAtRow( int iNoteRow ) const +{ + unsigned i; + for( i=0; i iNoteRow ) + break; + } + return static_cast(i); +} + bool TimingData::IsWarpAtRow( int iNoteRow ) const { if( m_WarpSegments.empty() ) @@ -477,6 +531,20 @@ bool TimingData::IsWarpAtRow( int iNoteRow ) const return false; } +bool TimingData::IsFakeAtRow( int iNoteRow ) const +{ + if( m_FakeSegments.empty() ) + return false; + + int i = GetFakeSegmentIndexAtRow( iNoteRow ); + const FakeSegment& s = m_FakeSegments[i]; + if( s.m_iStartRow <= iNoteRow && iNoteRow < BeatToNoteRow(s.m_fEndBeat) ) + { + return true; + } + return false; +} + int TimingData::GetTimeSignatureSegmentIndexAtRow( int iRow ) const { unsigned i; @@ -595,6 +663,16 @@ WarpSegment& TimingData::GetWarpSegmentAtRow( int iRow ) return m_WarpSegments[i]; } +FakeSegment& TimingData::GetFakeSegmentAtRow( int iRow ) +{ + static FakeSegment empty; + if( m_FakeSegments.empty() ) + return empty; + + int i = GetFakeSegmentIndexAtRow( iRow ); + return m_FakeSegments[i]; +} + int TimingData::GetTickcountSegmentIndexAtRow( int iRow ) const { unsigned i; @@ -952,6 +1030,25 @@ void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool m_SpeedSegments[i].m_iStartRow = lrintf( (iSegStart - iStartIndex) * fScale ) + iStartIndex; } + for( unsigned i = 0; i < m_FakeSegments.size(); i++ ) + { + const int iSegStartRow = m_FakeSegments[i].m_iStartRow; + const int iSegEndRow = BeatToNoteRow( m_FakeSegments[i].m_fEndBeat ); + if( iSegEndRow >= iStartIndex ) + { + if( iSegEndRow > iEndIndex ) + m_FakeSegments[i].m_fEndBeat += NoteRowToBeat(lrintf((iEndIndex - iStartIndex) * (fScale - 1))); + else + m_FakeSegments[i].m_fEndBeat = NoteRowToBeat(lrintf((iSegEndRow - iStartIndex) * fScale) + iStartIndex); + } + if( iSegStartRow < iStartIndex ) + continue; + else if( iSegStartRow > iEndIndex ) + m_FakeSegments[i].m_iStartRow += lrintf((iEndIndex - iStartIndex) * (fScale - 1)); + else + m_FakeSegments[i].m_iStartRow = lrintf((iSegStartRow - iStartIndex) * fScale) + iStartIndex; + } + // adjust BPM changes to preserve timing if( bAdjustBPM ) { @@ -1044,6 +1141,16 @@ void TimingData::InsertRows( int iStartRow, int iRowsToAdd ) continue; sped.m_iStartRow += iRowsToAdd; } + + for( unsigned i = 0; i < m_FakeSegments.size(); i++ ) + { + FakeSegment &fake = m_FakeSegments[i]; + if( BeatToNoteRow(fake.m_fEndBeat) >= iStartRow ) + fake.m_fEndBeat += NoteRowToBeat(iRowsToAdd); + if( fake.m_iStartRow < iStartRow ) + continue; + fake.m_iStartRow += iRowsToAdd; + } if( iStartRow == 0 ) { @@ -1217,6 +1324,26 @@ void TimingData::DeleteRows( int iStartRow, int iRowsToDelete ) } sped.m_iStartRow -= iRowsToDelete; } + + for( unsigned i = 0; i < m_FakeSegments.size(); i++ ) + { + FakeSegment &fake = m_FakeSegments[i]; + + if( BeatToNoteRow(fake.m_fEndBeat) >= iStartRow ) + fake.m_fEndBeat = max( NoteRowToBeat(iStartRow), fake.m_fEndBeat - NoteRowToBeat(iRowsToDelete) ); + + if( fake.m_iStartRow < iStartRow ) + continue; + + if( fake.m_iStartRow < iStartRow+iRowsToDelete ) + { + m_FakeSegments.erase( m_FakeSegments.begin()+i, m_FakeSegments.begin()+i+1 ); + --i; + continue; + } + + fake.m_iStartRow -= iRowsToDelete; + } this->SetBPMAtRow( iStartRow, fNewBPM ); } @@ -1292,6 +1419,11 @@ bool TimingData::HasWarps() const return m_WarpSegments.size()>0; } +bool TimingData::HasFakes() const +{ + return m_FakeSegments.size()>0; +} + bool TimingData::HasSpeedChanges() const { return m_SpeedSegments.size()>1; @@ -1343,6 +1475,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 HasFakes( T* p, lua_State *L ) { lua_pushboolean(L, p->HasFakes()); 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 ) { @@ -1431,6 +1564,7 @@ public: ADD_METHOD( HasStops ); ADD_METHOD( HasBPMChanges ); ADD_METHOD( HasWarps ); + ADD_METHOD( HasFakes ); ADD_METHOD( HasSpeedChanges ); ADD_METHOD( GetStops ); ADD_METHOD( GetDelays ); diff --git a/src/TimingData.h b/src/TimingData.h index b24cccdc5a..61ee14cd16 100644 --- a/src/TimingData.h +++ b/src/TimingData.h @@ -777,6 +777,113 @@ struct SpeedSegment bool operator>=( const SpeedSegment &other ) const { return !operator<(other); } }; +/** + * @brief Identifies when a whole region of arrows is to be ignored. + * + * FakeSegments are similar to the Fake Tap Notes in that the contents + * inside are neither for nor against the player. They can be useful for + * mission modes, in conjunction with WarpSegments, or perhaps other + * uses not thought up at the time of this comment. Unlike the Warp + * Segments, these are not magically jumped over: instead, these are + * drawn normally. + * + * These were inspired by the Pump It Up series. */ +struct FakeSegment +{ + /** + * @brief Create a simple Fake Segment with default values. + * + * It is best to override the values as soon as possible. + */ + FakeSegment() : m_iStartRow(-1), m_fEndBeat(-1) { } + /** + * @brief Create a Fake Segment with the specified values. + * @param s the starting row of this segment. + * @param r the row to be real again. + */ + FakeSegment( int s, int r ): m_iStartRow(max(0, (s < r ? s : r))), + m_fEndBeat(max(0, NoteRowToBeat((r > s ? r : s)))) {} + /** + * @brief Creates a Fake Segment with the specified values. + * @param s the starting row of this segment. + * @param b the beat to be real again. + */ + FakeSegment( int s, float b ): m_iStartRow(max(0, s)), + m_fEndBeat(max(0, b)) {} + /** + * @brief Create a Fake Segment with the specified values. + * @param s the starting beat in this segment. + * @param r the row to be real again. + */ + FakeSegment( float s, int r ): + m_iStartRow(max(0, BeatToNoteRow(s))), + m_fEndBeat(max(0, NoteRowToBeat(r))) {} + /** + * @brief Creates a Fake Segment with the specified values. + * @param s the starting beat of this segment. + * @param b the beat to be real again. + */ + FakeSegment( float s, float b ): + m_iStartRow(max(0, BeatToNoteRow((s < b ? s : b)))), + m_fEndBeat(max(0, (b > s ? b : s))) {} + /** + * @brief The row in which the FakeSegment activates. + */ + int m_iStartRow; + /** + * @brief The beat that ends the FakeSegment. + */ + float m_fEndBeat; + /** + * @brief Compares two FakeSegments to see if they are equal to each other. + * @param other the other FakeSegment to compare to. + * @return the equality of the two segments. + */ + bool operator==( const FakeSegment &other ) const + { + COMPARE( m_iStartRow ); + COMPARE( m_fEndBeat ); + return true; + } + /** + * @brief Compares two FakeSegments to see if they are not equal to each other. + * @param other the other FakeSegment to compare to. + * @return the inequality of the two segments. + */ + bool operator!=( const FakeSegment &other ) const { return !operator==(other); } + /** + * @brief Compares two FakeSegments to see if one is less than the other. + * @param other the other FakeSegment to compare to. + * @return the truth/falsehood of if the first is less than the second. + */ + bool operator<( const FakeSegment &other ) const + { + return m_iStartRow < other.m_iStartRow || + ( m_iStartRow == other.m_iStartRow && m_fEndBeat < other.m_fEndBeat ); + } + /** + * @brief Compares two FakeSegments to see if one is less than or equal to the other. + * @param other the other FakeSegment to compare to. + * @return the truth/falsehood of if the first is less or equal to than the second. + */ + bool operator<=( const FakeSegment &other ) const + { + return ( operator<(other) || operator==(other) ); + } + /** + * @brief Compares two FakeSegments to see if one is greater than the other. + * @param other the other FakeSegment to compare to. + * @return the truth/falsehood of if the first is greater than the second. + */ + bool operator>( const FakeSegment &other ) const { return !operator<=(other); } + /** + * @brief Compares two FakeSegments to see if one is greater than or equal to the other. + * @param other the other FakeSegment to compare to. + * @return the truth/falsehood of if the first is greater than or equal to the second. + */ + bool operator>=( const FakeSegment &other ) const { return !operator<(other); } +}; + /** * @brief Holds data for translating beats<->seconds. @@ -1487,6 +1594,72 @@ public: */ void AddSpeedSegment( const SpeedSegment &seg ); + /** + * @brief Determine when the fakes end. + * @param iRow The row you start on. + * @return the time when the fakes end. + */ + float GetFakeAtRow( int iRow ) const; + /** + * @brief Determine when the fakes end. + * @param fBeat The beat you start on. + * @return the time when the fakes end. + */ + float GetFakeAtBeat( float fBeat ) const { return GetFakeAtRow( BeatToNoteRow( fBeat ) ); } + /** + * @brief Set the beat to indicate when the FakeSegment ends. + * @param iRow The row to start on. + * @param fNew The destination beat. + */ + void SetFakeAtRow( int iRow, float fNew ); + /** + * @brief Set the beat to indicate when the FakeSegment ends. + * @param fBeat The beat to start on. + * @param fNew The destination beat. + */ + void SetFakeAtBeat( float fBeat, float fNew ) { SetFakeAtRow( BeatToNoteRow( fBeat ), fNew ); } + /** + * @brief Retrieve the FakeSegment at the specified row. + * @param iRow the row to focus on. + * @return the FakeSegment in question. + */ + FakeSegment& GetFakeSegmentAtRow( int iRow ); + /** + * @brief Retrieve the FakeSegment at the specified beat. + * @param fBeat the beat to focus on. + * @return the FakeSegment in question. + */ + FakeSegment& GetFakeSegmentAtBeat( float fBeat ) { return GetFakeSegmentAtRow( BeatToNoteRow( fBeat ) ); } + /** + * @brief Retrieve the index of the FakeSegment at the specified row. + * @param iRow the row to focus on. + * @return the index in question. + */ + int GetFakeSegmentIndexAtRow( int iRow ) const; + /** + * @brief Retrieve the index of the FakeSegment at the specified beat. + * @param fBeat the beat to focus on. + * @return the index in question. + */ + int GetFakeSegmentIndexAtBeat( float fBeat ) const { return GetFakeSegmentIndexAtRow( BeatToNoteRow( fBeat ) ); } + /** + * @brief Checks if the row is inside a fake. + * @param iRow the row to focus on. + * @return true if the row is inside a fake, false otherwise. + */ + bool IsFakeAtRow( int iRow ) const; + /** + * @brief Checks if the beat is inside a fake. + * @param fBeat the beat to focus on. + * @return true if the row is inside a fake, false otherwise. + */ + bool IsFakeAtBeat( float fBeat ) const { return IsFakeAtRow( BeatToNoteRow( fBeat ) ); } + /** + * @brief Add the FakeSegment to the TimingData. + * @param seg the new FakeSegment. + */ + void AddFakeSegment( const FakeSegment &seg ); + void MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor ); @@ -1531,6 +1704,10 @@ public: * @return true if there is at least one warp, false otherwise. */ bool HasWarps() const; + /** + * @brief View the TimingData to see if there is at least one fake segment involved. + * @return true if there is at least one fake segment, false otherwise. */ + bool HasFakes() 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. */ @@ -1566,6 +1743,9 @@ public: COMPARE( m_SpeedSegments.size() ); for( unsigned i=0; i m_LabelSegments; /** @brief The collection of SpeedSegments. */ vector m_SpeedSegments; + /** @brief The collection of FakeSegments. */ + vector m_FakeSegments; /** * @brief The initial offset of a song. */