diff --git a/Docs/Changelog_SSCformat.txt b/Docs/Changelog_SSCformat.txt index e5bcf25049..e234367dde 100644 --- a/Docs/Changelog_SSCformat.txt +++ b/Docs/Changelog_SSCformat.txt @@ -14,6 +14,7 @@ ________________________________________________________________________________ * #SPEEDS tag for modified scroll speeds. * #FAKES tag for ignoring judgments within a range. * #WARPS modified so that second parameter is now relative to the start. +* #SCROLLS tag for...hilarity? [v0.59] - Wolfman2000 * Typo fix: #RADARVALUES needed a semicolon at the end, not a colon. diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index ae5db43ca5..17065352a0 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -8,6 +8,11 @@ ________________________________________________________________________________ StepMania 5.0 $NEXT | 20110xyy -------------------------------------------------------------------------------- +2011/05/25 +---------- +* [NotesLoaderSSC/NotesWriterSSC] Added the ScrollSegments. Think BPM change, + but not. [theDtTvB] + 2011/05/23 ---------- * [Steps] Added the UsesSplitTiming Lua binding. [Wolfman2000] diff --git a/Docs/SimfileFormats/ssc_msd5.txt b/Docs/SimfileFormats/ssc_msd5.txt index 64bad9126e..845844fbc6 100644 --- a/Docs/SimfileFormats/ssc_msd5.txt +++ b/Docs/SimfileFormats/ssc_msd5.txt @@ -58,6 +58,7 @@ #TICKCOUNTS:; #COMBOS:; #SPEEDS:; +#SCROLLS:; #FAKES:; #LABELS:; #ATTACKS:; // Not yet in use. diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 818a5bb456..1884302235 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -855,6 +855,7 @@ Edit warp=Edit warp Edit speed (percent)=Edit speed (percent) Edit speed (wait)=Edit speed (length) Edit speed (mode)=Edit speed (mode) +Edit scrolling factor=Edit scrolling factor Edit fake=Edit fake segment Editor options=Options EditorShowBGChangesPlay=Show Backgrounds @@ -1233,6 +1234,7 @@ Enter a new Warp value.=Enter the number of beats you will warp over.\n\nType "0 Enter a new Speed percent value.=Enter the ratio for speed scrolling.\nEnter the previous value to remove.\n1 is the default scroll. Enter a new Speed wait value.=Enter how long in seconds/beats it takes to change.\n\n0 is instant. Enter a new Speed mode value.=Enter how this segment is handled.\n\nType "0" for beats, "1" for seconds. +Enter a new Scroll value.=Enter a new Scroll value. Enter a new Fake value.=Enter the number of beats that won't be judged.\n\nType "0" to remove this. Are you sure you want to erase this chart's timing data?=Are you sure you want to erase this chart's timing data? Enter a new artist transliteration.=Enter a new artist transliteration. diff --git a/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index dcd6d3423d..73521fb0cc 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -991,6 +991,7 @@ TickcountColor=color("0,1,0,1") ComboColor=color("0.55,1,0,1") LabelColor=color("1,0,0,1") SpeedColor=color("0.5,1,1,1") +ScrollColor=color("0.3,0.8,1,1") FakeColor=color("1,1,0.5,1") # BPMIsLeftSide=true @@ -1002,6 +1003,7 @@ TickcountIsLeftSide=false ComboIsLeftSide=false LabelIsLeftSide=false SpeedIsLeftSide=false +ScrollIsLeftSide=true FakeIsLeftSide=true # BPMOffsetX=60 @@ -1013,6 +1015,7 @@ TickcountOffsetX=70 ComboOffsetX=50 LabelOffsetX=130 SpeedOffsetX=30 +ScrollOffsetX=40 FakeOffsetX=90 [PlayerStageStats] diff --git a/src/ArrowEffects.cpp b/src/ArrowEffects.cpp index 2e3a1b6144..fb8da38fb5 100644 --- a/src/ArrowEffects.cpp +++ b/src/ArrowEffects.cpp @@ -231,9 +231,12 @@ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float * entirely time spacing (respectively). Occasionally, we tween between them. */ if( pPlayerState->m_PlayerOptions.GetCurrent().m_fTimeSpacing != 1.0f ) { + float bShowEffects = !( GAMESTATE->m_bInStepEditor || !GAMESTATE->m_bIsUsingStepTiming ); float fBeatsUntilStep = fNoteBeat - fSongBeat; + if( bShowEffects ) + fBeatsUntilStep = pCurSteps->m_Timing.GetDisplayedBeat(fNoteBeat) - pCurSteps->m_Timing.GetDisplayedBeat(fSongBeat); float fYOffsetBeatSpacing = fBeatsUntilStep; - float fSpeedMultiplier = ( GAMESTATE->m_bInStepEditor || !GAMESTATE->m_bIsUsingStepTiming ) ? 1.0 : pCurSteps->m_Timing.GetDisplayedSpeedPercent( position.m_fSongBeatVisible, position.m_fMusicSecondsVisible ); + float fSpeedMultiplier = bShowEffects ? pCurSteps->m_Timing.GetDisplayedSpeedPercent( position.m_fSongBeatVisible, position.m_fMusicSecondsVisible ) : 1.0; fYOffset += fSpeedMultiplier * fYOffsetBeatSpacing * (1-pPlayerState->m_PlayerOptions.GetCurrent().m_fTimeSpacing); } diff --git a/src/NoteField.cpp b/src/NoteField.cpp index 4266675887..fb1ac42670 100644 --- a/src/NoteField.cpp +++ b/src/NoteField.cpp @@ -455,6 +455,7 @@ static ThemeMetric TICKCOUNT_COLOR ( "NoteField", "TickcountColor" ); static ThemeMetric COMBO_COLOR ( "NoteField", "ComboColor" ); static ThemeMetric LABEL_COLOR ( "NoteField", "LabelColor" ); static ThemeMetric SPEED_COLOR ( "NoteField", "SpeedColor" ); +static ThemeMetric SCROLL_COLOR ( "NoteField", "ScrollColor" ); static ThemeMetric FAKE_COLOR ("NoteField", "FakeColor" ); static ThemeMetric BPM_IS_LEFT_SIDE ( "NoteField", "BPMIsLeftSide" ); static ThemeMetric STOP_IS_LEFT_SIDE ( "NoteField", "StopIsLeftSide" ); @@ -465,6 +466,7 @@ static ThemeMetric TICKCOUNT_IS_LEFT_SIDE ( "NoteField", "TickcountIsLeftS static ThemeMetric COMBO_IS_LEFT_SIDE ( "NoteField", "ComboIsLeftSide" ); static ThemeMetric LABEL_IS_LEFT_SIDE ( "NoteField", "LabelIsLeftSide" ); static ThemeMetric SPEED_IS_LEFT_SIDE ( "NoteField", "SpeedIsLeftSide" ); +static ThemeMetric SCROLL_IS_LEFT_SIDE ( "NoteField", "ScrollIsLeftSide" ); static ThemeMetric FAKE_IS_LEFT_SIDE ( "NoteField", "FakeIsLeftSide" ); static ThemeMetric BPM_OFFSETX ( "NoteField", "BPMOffsetX" ); static ThemeMetric STOP_OFFSETX ( "NoteField", "StopOffsetX" ); @@ -475,6 +477,7 @@ static ThemeMetric TICKCOUNT_OFFSETX ( "NoteField", "TickcountOffsetX" ); static ThemeMetric COMBO_OFFSETX ( "NoteField", "ComboOffsetX" ); static ThemeMetric LABEL_OFFSETX ( "NoteField", "LabelOffsetX" ); static ThemeMetric SPEED_OFFSETX ( "NoteField", "SpeedOffsetX" ); +static ThemeMetric SCROLL_OFFSETX ( "NoteField", "ScrollOffsetX" ); static ThemeMetric FAKE_OFFSETX ( "NoteField", "FakeOffsetX" ); void NoteField::DrawBPMText( const float fBeat, const float fBPM ) @@ -622,6 +625,23 @@ void NoteField::DrawSpeedText( const float fBeat, float fPercent, float fWait, u m_textMeasureNumber.Draw(); } +void NoteField::DrawScrollText( const float fBeat, float fPercent ) +{ + const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, 0, fBeat ); + const float fYPos = ArrowEffects::GetYPos( m_pPlayerState, 0, fYOffset, m_fYReverseOffsetPixels ); + const float fZoom = ArrowEffects::GetZoom( m_pPlayerState ); + const float xBase = GetWidth()/2.f; + const float xOffset = SCROLL_OFFSETX * fZoom; + + m_textMeasureNumber.SetZoom( fZoom ); + m_textMeasureNumber.SetHorizAlign( SCROLL_IS_LEFT_SIDE ? align_right : align_left ); + m_textMeasureNumber.SetDiffuse( SCROLL_COLOR ); + m_textMeasureNumber.SetGlow( RageColor(1,1,1,RageFastCos(RageTimer::GetTimeSinceStartFast()*2)/2+0.5f) ); + m_textMeasureNumber.SetText( ssprintf("%.3fx", fPercent) ); + m_textMeasureNumber.SetXY( (SCROLL_IS_LEFT_SIDE ? -xBase - xOffset : xBase + xOffset), fYPos ); + m_textMeasureNumber.Draw(); +} + void NoteField::DrawFakeText( const float fBeat, const float fNewBeat ) { const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, 0, fBeat ); @@ -973,6 +993,20 @@ void NoteField::DrawPrimitives() } } + // Scroll text + if( GAMESTATE->m_bIsUsingStepTiming ) + { + FOREACH_CONST( ScrollSegment, timing.m_ScrollSegments, seg ) + { + if( seg->m_iStartRow >= iFirstRowToDraw && seg->m_iStartRow <= iLastRowToDraw ) + { + float fBeat = NoteRowToBeat(seg->m_iStartRow); + if( IS_ON_SCREEN(fBeat) ) + DrawScrollText( fBeat, seg->m_fPercent ); + } + } + } + // Speed text if( GAMESTATE->m_bIsUsingStepTiming ) { diff --git a/src/NoteField.h b/src/NoteField.h index a4193f41a5..3357eff284 100644 --- a/src/NoteField.h +++ b/src/NoteField.h @@ -65,6 +65,7 @@ protected: void DrawComboText( const float fBeat, int iCombo ); void DrawLabelText( const float fBeat, RString sLabel ); void DrawSpeedText( const float fBeat, float fPercent, float fWait, unsigned short usMode ); + void DrawScrollText( const float fBeat, float fPercent ); void DrawFakeText( const float fBeat, const float fNewBeat ); void DrawAttackText( const float fBeat, const Attack &attack ); void DrawBGChangeText( const float fBeat, const RString sNewBGName ); diff --git a/src/NotesLoaderSSC.cpp b/src/NotesLoaderSSC.cpp index 81969e336e..e73fd69158 100644 --- a/src/NotesLoaderSSC.cpp +++ b/src/NotesLoaderSSC.cpp @@ -176,6 +176,36 @@ void SSCLoader::ProcessSpeeds( TimingData &out, const RString sParam ) } } +void SSCLoader::ProcessScrolls( TimingData &out, const RString sParam ) +{ + vector vs1; + split( sParam, ",", vs1 ); + + FOREACH_CONST( RString, vs1, s1 ) + { + vector vs2; + split( *s1, "=", vs2 ); + + if( vs2.size() < 2 ) + { + LOG->UserLog( "Song file", "(UNKNOWN)", "has an scroll change with %i values.", (int)vs2.size() ); + continue; + } + + const float fBeat = StringToFloat( vs2[0] ); + + ScrollSegment seg( fBeat, StringToFloat( vs2[1] ) ); + + if( fBeat < 0 ) + { + LOG->UserLog( "Song file", "(UNKNOWN)", "has an scroll change with beat %f.", fBeat ); + continue; + } + + out.AddScrollSegment( seg ); + } +} + void SSCLoader::ProcessFakes( TimingData &out, const RString sParam ) { vector arrayFakeExpressions; @@ -601,6 +631,11 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach ProcessSpeeds( stepsTiming, sParams[1] ); } + else if( sValueName=="SCROLLS" ) + { + ProcessScrolls( stepsTiming, sParams[1] ); + } + else if( sValueName=="FAKES" ) { ProcessFakes( stepsTiming, sParams[1] ); diff --git a/src/NotesLoaderSSC.h b/src/NotesLoaderSSC.h index cccf26c45f..936a24e219 100644 --- a/src/NotesLoaderSSC.h +++ b/src/NotesLoaderSSC.h @@ -81,6 +81,7 @@ namespace SSCLoader void ProcessLabels( TimingData &, const RString ); void ProcessCombos( TimingData &, const RString ); void ProcessSpeeds( TimingData &, const RString ); + void ProcessScrolls( TimingData &, const RString ); void ProcessFakes( TimingData &, const RString ); } #endif diff --git a/src/NotesWriterSSC.cpp b/src/NotesWriterSSC.cpp index 77578908f8..468f26da1c 100644 --- a/src/NotesWriterSSC.cpp +++ b/src/NotesWriterSSC.cpp @@ -137,6 +137,11 @@ static void GetTimingTags( vector &lines, TimingData timing, bool bIsSo w.Write( ss->m_iStartRow, ss->m_fPercent, ss->m_fWait, ss->m_usMode ); w.Finish(); + w.Init( "SCROLLS" ); + FOREACH_CONST( ScrollSegment, timing.m_ScrollSegments, ss ) + w.Write( ss->m_iStartRow, ss->m_fPercent ); + w.Finish(); + if( !bIsSong ) { w.Init( "FAKES" ); diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index 0c8f3b6427..48b87e1996 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -85,6 +85,7 @@ AutoScreenMessage( SM_BackFromWarpChange ); AutoScreenMessage( SM_BackFromSpeedPercentChange ); AutoScreenMessage( SM_BackFromSpeedWaitChange ); AutoScreenMessage( SM_BackFromSpeedModeChange ); +AutoScreenMessage( SM_BackFromScrollChange ); AutoScreenMessage( SM_BackFromFakeChange ); AutoScreenMessage( SM_DoEraseStepTiming ); AutoScreenMessage( SM_DoSaveAndExit ); @@ -581,6 +582,7 @@ static MenuDef g_TimingDataInformation( MenuRowDef( ScreenEdit::speed_percent, "Edit speed (percent)", true, EditMode_Full, true, true, 0, NULL ), MenuRowDef( ScreenEdit::speed_wait, "Edit speed (wait)", true, EditMode_Full, true, true, 0, NULL ), MenuRowDef( ScreenEdit::speed_mode, "Edit speed (mode)", true, EditMode_Full, true, true, 0, "Beats", "Seconds" ), + MenuRowDef( ScreenEdit::scroll, "Edit scrolling factor", true, EditMode_Full, true, true, 0, NULL ), MenuRowDef( ScreenEdit::fake, "Edit fake", true, EditMode_Full, true, true, 0, NULL ), MenuRowDef( ScreenEdit::erase_step_timing, "Erase step timing", true, EditMode_Full, true, true, 0, NULL ) ); @@ -2795,6 +2797,12 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) } SetDirty( true ); } + else if( SM == SM_BackFromScrollChange ) + { + float fNum = StringToFloat( ScreenTextEntry::s_sLastAnswer ); + GetAppropriateTiming().SetScrollAtBeat( GetBeat(), fNum ); + SetDirty( true ); + } else if ( SM == SM_BackFromFakeChange ) { float fFake = StringToFloat( ScreenTextEntry::s_sLastAnswer ); @@ -3225,6 +3233,7 @@ void ScreenEdit::DisplayTimingMenu() RString starting = ( pTime.GetSpeedModeAtBeat( fBeat ) == 1 ? "Seconds" : "Beats" ); g_TimingDataInformation.rows[speed_mode].SetOneUnthemedChoice( starting.c_str() ); + g_TimingDataInformation.rows[scroll].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetScrollAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[fake].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetFakeAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[tickcount].bEnabled = GAMESTATE->m_bIsUsingStepTiming; @@ -3913,6 +3922,7 @@ static LocalizedString ENTER_WARP_VALUE ( "ScreenEdit", "Enter a new Warp val static LocalizedString ENTER_SPEED_PERCENT_VALUE ( "ScreenEdit", "Enter a new Speed percent value." ); static LocalizedString ENTER_SPEED_WAIT_VALUE ( "ScreenEdit", "Enter a new Speed wait value." ); static LocalizedString ENTER_SPEED_MODE_VALUE ( "ScreenEdit", "Enter a new Speed mode value." ); +static LocalizedString ENTER_SCROLL_VALUE ( "ScreenEdit", "Enter a new Scroll value." ); static LocalizedString ENTER_FAKE_VALUE ( "ScreenEdit", "Enter a new Fake value." ); static LocalizedString CONFIRM_TIMING_ERASE ( "ScreenEdit", "Are you sure you want to erase this chart's timing data?" ); void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice c, const vector &iAnswers ) @@ -3997,6 +4007,14 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice 10 ); break; + case scroll: + ScreenTextEntry::TextEntry( + SM_BackFromScrollChange, + ENTER_SCROLL_VALUE, + ssprintf( "%.5f", GetAppropriateTiming().GetScrollSegmentAtBeat( GetBeat() ).m_fPercent ), + 10 + ); + break; case speed_wait: ScreenTextEntry::TextEntry( SM_BackFromSpeedWaitChange, diff --git a/src/ScreenEdit.h b/src/ScreenEdit.h index d4ac2af111..498edc2f7d 100644 --- a/src/ScreenEdit.h +++ b/src/ScreenEdit.h @@ -498,6 +498,7 @@ public: speed_percent, speed_wait, speed_mode, + scroll, fake, erase_step_timing, NUM_TIMING_DATA_INFORMATION_CHOICES diff --git a/src/Song.cpp b/src/Song.cpp index 3a962e72c4..059190c751 100644 --- a/src/Song.cpp +++ b/src/Song.cpp @@ -41,7 +41,7 @@ * @brief The internal version of the cache for StepMania. * * Increment this value to invalidate the current cache. */ -const int FILE_CACHE_VERSION = 176; +const int FILE_CACHE_VERSION = 177; /** @brief How long does a song sample last by default? */ const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f; diff --git a/src/TimingData.cpp b/src/TimingData.cpp index 24035ab690..1a8c0e9802 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::AddScrollSegment( const ScrollSegment &seg ) +{ + m_ScrollSegments.insert( upper_bound(m_ScrollSegments.begin(), m_ScrollSegments.end(), seg), seg ); +} + void TimingData::AddFakeSegment( const FakeSegment &seg ) { m_FakeSegments.insert( upper_bound(m_FakeSegments.begin(), m_FakeSegments.end(), seg), seg ); @@ -290,6 +295,34 @@ void TimingData::SetSpeedAtRow( int iRow, float fPercent, float fWait, unsigned } } +void TimingData::SetScrollAtRow( int iRow, float fPercent ) +{ + unsigned i; + for( i = 0; i < m_ScrollSegments.size(); i++ ) + { + if( m_ScrollSegments[i].m_iStartRow >= iRow) + break; + } + + if ( i == m_ScrollSegments.size() || m_ScrollSegments[i].m_iStartRow != iRow ) + { + // the core mod itself matters the most for comparisons. + if( i == 0 || m_ScrollSegments[i-1].m_fPercent != fPercent ) + AddScrollSegment( ScrollSegment(iRow, fPercent) ); + } + else + { + // The others aren't compared: only the mod itself matters. + if( i > 0 && m_ScrollSegments[i-1].m_fPercent == fPercent ) + m_ScrollSegments.erase( m_ScrollSegments.begin()+i, + m_ScrollSegments.begin()+i+1 ); + else + { + m_ScrollSegments[i].m_fPercent = fPercent; + } + } +} + void TimingData::SetFakeAtRow( int iRow, float fNew ) { unsigned i; @@ -400,6 +433,11 @@ unsigned short TimingData::GetSpeedModeAtRow( int iRow ) return GetSpeedSegmentAtRow( iRow ).m_usMode; } +float TimingData::GetScrollAtRow( int iRow ) +{ + return GetScrollSegmentAtRow( iRow ).m_fPercent; +} + float TimingData::GetFakeAtRow( int iFakeRow ) const { for( unsigned i=0; i(i); } +int TimingData::GetScrollSegmentIndexAtRow( int iRow ) const +{ + unsigned i; + for (i=0; i < m_ScrollSegments.size() - 1; i++ ) + if( m_ScrollSegments[i+1].m_iStartRow > iRow ) + break; + return static_cast(i); +} + BPMSegment& TimingData::GetBPMSegmentAtRow( int iNoteRow ) { static BPMSegment empty; @@ -613,6 +660,15 @@ SpeedSegment& TimingData::GetSpeedSegmentAtRow( int iRow ) return m_SpeedSegments[i]; } +ScrollSegment& TimingData::GetScrollSegmentAtRow( int iRow ) +{ + unsigned i; + for( i=0; i iRow ) + break; + return m_ScrollSegments[i]; +} + int TimingData::GetTimeSignatureNumeratorAtRow( int iRow ) { return GetTimeSignatureSegmentAtRow( iRow ).m_iNumerator; @@ -943,6 +999,17 @@ float TimingData::GetElapsedTimeFromBeatNoOffset( float fBeat ) const } +float TimingData::GetDisplayedBeat( float fBeat ) const +{ + unsigned index = GetScrollSegmentIndexAtBeat(fBeat); + float fOutBeat = ( fBeat - NoteRowToBeat(m_ScrollSegments[index].m_iStartRow) ) * m_ScrollSegments[index].m_fPercent; + for( unsigned i = 0; i < index; i ++ ) + { + fOutBeat += ( NoteRowToBeat(m_ScrollSegments[i + 1].m_iStartRow) - NoteRowToBeat(m_ScrollSegments[i].m_iStartRow) ) * m_ScrollSegments[i].m_fPercent; + } + return fOutBeat; +} + void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool bAdjustBPM ) { ASSERT( fScale > 0 ); @@ -1436,6 +1503,13 @@ void TimingData::TidyUpData() SpeedSegment seg(0, 1, 0); m_SpeedSegments.push_back( seg ); } + + // Always be sure there is a starting scrolling factor. + if( m_ScrollSegments.empty() ) + { + ScrollSegment seg(0, 1); + m_ScrollSegments.push_back( seg ); + } } @@ -1467,6 +1541,11 @@ bool TimingData::HasSpeedChanges() const return m_SpeedSegments.size()>1; } +bool TimingData::HasScrollChanges() const +{ + return m_ScrollSegments.size()>1; +} + void TimingData::NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const { iMeasureIndexOut = 0; diff --git a/src/TimingData.h b/src/TimingData.h index c44b4da58a..e65bcfbf16 100644 --- a/src/TimingData.h +++ b/src/TimingData.h @@ -777,6 +777,82 @@ struct SpeedSegment bool operator>=( const SpeedSegment &other ) const { return !operator<(other); } }; +/** + * @brief Identifies when the chart scroll changes. + * + * ScrollSegments adjusts the scrolling speed of the note field. + * Unlike forced attacks, these cannot be turned off at a set time: + * reset it by setting the precentage back to 1. + * + * These were inspired by the Pump It Up series. */ +struct ScrollSegment +{ + /** @brief Sets up the ScrollSegment with default values. */ + ScrollSegment(): m_iStartRow(0), m_fPercent(1) {} + + /** + * @brief Sets up the ScrollSegment with specified values. + * @param i The row this activates. + * @param p The percentage to use. */ + ScrollSegment(int i, float p): m_iStartRow(i), m_fPercent(p) {} + + /** + * @brief Sets up the ScrollSegment with specified values. + * @param r The beat this activates. + * @param p The percentage to use. */ + ScrollSegment(float r, float p): m_iStartRow(BeatToNoteRow(r)), m_fPercent(p) {} + + /** @brief The row in which the ScrollSegment activates. */ + int m_iStartRow; + /** @brief The percentage to use when multiplying the chart's scroll rate. */ + float m_fPercent; + + /** + * @brief Compares two ScrollSegment to see if they are equal to each other. + * @param other the other ScrollSegment to compare to. + * @return the equality of the two segments. + */ + bool operator==( const ScrollSegment &other ) const + { + COMPARE( m_iStartRow ); + COMPARE( m_fPercent ); + return true; + } + /** + * @brief Compares two ScrollSegment to see if they are not equal to each other. + * @param other the other ScrollSegment to compare to. + * @return the inequality of the two segments. + */ + bool operator!=( const ScrollSegment &other ) const { return !operator==(other); } + /** + * @brief Compares two ScrollSegment to see if one is less than the other. + * @param other the other ScrollSegment to compare to. + * @return the truth/falsehood of if the first is less than the second. + */ + bool operator<( const ScrollSegment &other ) const { return m_iStartRow < other.m_iStartRow; } + /** + * @brief Compares two ScrollSegment to see if one is less than or equal to the other. + * @param other the other ScrollSegment to compare to. + * @return the truth/falsehood of if the first is less or equal to than the second. + */ + bool operator<=( const ScrollSegment &other ) const + { + return ( operator<(other) || operator==(other) ); + } + /** + * @brief Compares two ScrollSegment to see if one is greater than the other. + * @param other the other ScrollSegment to compare to. + * @return the truth/falsehood of if the first is greater than the second. + */ + bool operator>( const ScrollSegment &other ) const { return !operator<=(other); } + /** + * @brief Compares two ScrollSegment to see if one is greater than or equal to the other. + * @param other the other ScrollSegment to compare to. + * @return the truth/falsehood of if the first is greater than or equal to the second. + */ + bool operator>=( const ScrollSegment &other ) const { return !operator<(other); } +}; + /** * @brief Identifies when a whole region of arrows is to be ignored. * @@ -1596,6 +1672,65 @@ public: float GetDisplayedSpeedPercent( float fBeat, float fMusicSeconds ) const; + /** + * @brief Retrieve the scrolling factor at the given row. + * @param iNoteRow the row in question. + * @return the percent. + */ + float GetScrollAtRow( int iNoteRow ); + /** + * @brief Retrieve the scrolling factor at the given beat. + * @param fBeat the beat in question. + * @return the percent. + */ + float GetScrollAtBeat( float fBeat ) { return GetScrollAtRow( BeatToNoteRow(fBeat) ); } + + /** + * @brief Set the row to have the new Scrolling factor. + * @param iNoteRow the row to have the new Speed. + * @param fPercent the scrolling factor. + */ + void SetScrollAtRow( int iNoteRow, float fPercent ); + /** + * @brief Set the row to have the new Scrolling factor. + * @param iNoteRow the row to have the new Speed. + * @param fPercent the scrolling factor. + */ + void SetScrollAtBeat( float fBeat, float fPercent ) { SetScrollAtRow( BeatToNoteRow(fBeat), fPercent ); } + + /** + * @brief Retrieve the ScrollSegment at the specified row. + * @param iNoteRow the row that has a ScrollSegment. + * @return the ScrollSegment in question. + */ + ScrollSegment& GetScrollSegmentAtRow( int iNoteRow ); + /** + * @brief Retrieve the ScrollSegment at the specified beat. + * @param fBeat the beat that has a ScrollSegment. + * @return the ScrollSegment in question. + */ + ScrollSegment& GetScrollSegmentAtBeat( float fBeat ) { return GetScrollSegmentAtRow( BeatToNoteRow(fBeat) ); } + + /** + * @brief Retrieve the index of the ScrollSegment at the specified row. + * @param iNoteRow the row that has a ScrollSegment. + * @return the ScrollSegment's index in question. + */ + int GetScrollSegmentIndexAtRow( int iNoteRow ) const; + /** + * @brief Retrieve the index of the ScrollSegment at the specified beat. + * @param fBeat the beat that has a ScrollSegment. + * @return the ScrollSegment's index in question. + */ + int GetScrollSegmentIndexAtBeat( float fBeat ) const { return GetScrollSegmentIndexAtRow( BeatToNoteRow(fBeat) ); } + + /** + * @brief Add the ScrollSegment to the TimingData. + * @param seg the new ScrollSegment. + */ + void AddScrollSegment( const ScrollSegment &seg ); + + /** * @brief Determine when the fakes end. * @param iRow The row you start on. @@ -1689,6 +1824,7 @@ public: return fBeat; } float GetElapsedTimeFromBeatNoOffset( float fBeat ) const; + float GetDisplayedBeat( float fBeat ) const; /** * @brief View the TimingData to see if a song changes its BPM at any point. * @return true if there is at least one change, false otherwise. @@ -1714,6 +1850,10 @@ public: * @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 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 HasScrollChanges() const; /** * @brief Compare two sets of timing data to see if they are equal. * @param other the other TimingData. @@ -1745,6 +1885,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 ScrollSegments. */ + vector m_ScrollSegments; /** @brief The collection of FakeSegments. */ vector m_FakeSegments; /**