From 0994bd64bc1248739092ec7924c862c10fac1780 Mon Sep 17 00:00:00 2001 From: Thai Pangsakulyanont Date: Tue, 24 May 2011 11:12:47 +0700 Subject: [PATCH 01/12] fix crash in NotesLoaderBMS --- src/NotesLoaderBMS.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/NotesLoaderBMS.cpp b/src/NotesLoaderBMS.cpp index b87a603042..3f0f2fb394 100644 --- a/src/NotesLoaderBMS.cpp +++ b/src/NotesLoaderBMS.cpp @@ -1183,14 +1183,17 @@ bool BMSLoader::LoadFromDir( const RString &sDir, Song &out ) Steps* pNewNotes = apSteps[i]; const bool ok = LoadFromBMSFile( out.GetSongDir() + arrayBMSFileNames[i], aBMSData[i], *pNewNotes, out, mapFilenameToKeysoundIndex ); if( ok ) + { + // set song's timing data to the main file. + if( i == iMainDataIndex ) + out.m_SongTiming = pNewNotes->m_Timing; + out.AddSteps( pNewNotes ); + } else delete pNewNotes; } - // set song's timing data to the main file. - out.m_SongTiming = apSteps[iMainDataIndex]->m_Timing; - SlideDuplicateDifficulties( out ); ConvertString( out.m_sMainTitle, "utf-8,japanese" ); From aa0eb7f5d72fd26e32dee21788cc3506318f212a Mon Sep 17 00:00:00 2001 From: Thai Pangsakulyanont Date: Tue, 24 May 2011 11:43:08 +0700 Subject: [PATCH 02/12] GetSpeedMultiplier in ArrowEffects becomes TimingData::GetDisplayedSpeedPercent. We might need to use it later in other parts (NoteField). --- src/ArrowEffects.cpp | 46 +------------------------------------------- src/TimingData.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++ src/TimingData.h | 2 ++ 3 files changed, 47 insertions(+), 45 deletions(-) diff --git a/src/ArrowEffects.cpp b/src/ArrowEffects.cpp index e408967789..2e3a1b6144 100644 --- a/src/ArrowEffects.cpp +++ b/src/ArrowEffects.cpp @@ -211,50 +211,6 @@ void ArrowEffects::Update() } } -float GetSpeedMultiplier( float fSongBeat, float fMusicSeconds, const TimingData &tim ) -{ - if( tim.m_SpeedSegments.size() == 0 ) - return 1.0; - - const int index = tim.GetSpeedSegmentIndexAtBeat( fSongBeat ); - - const SpeedSegment &seg = tim.m_SpeedSegments[index]; - float fStartBeat = NoteRowToBeat(seg.m_iStartRow); - float fStartTime = tim.GetElapsedTimeFromBeat( fStartBeat ) - tim.GetDelayAtBeat( fStartBeat ); - float fEndTime; - float fCurTime = fMusicSeconds; - - if( seg.m_usMode == 1 ) // seconds - { - fEndTime = fStartTime + seg.m_fWait; - } - else - { - fEndTime = tim.GetElapsedTimeFromBeat( fStartBeat + seg.m_fWait ) - tim.GetDelayAtBeat( fStartBeat + seg.m_fWait ); - } - - if( ( index == 0 && tim.m_SpeedSegments[0].m_fWait > 0.0 ) && fCurTime < fStartTime ) - { - return 1.0; - } - else if( fEndTime >= fCurTime && ( index > 0 || tim.m_SpeedSegments[0].m_fWait > 0.0 ) ) - { - const float fPriorSpeed = ( index == 0 ? 1 : tim.m_SpeedSegments[index - 1].m_fPercent ); - float fTimeUsed = fCurTime - fStartTime; - float fDuration = fEndTime - fStartTime; - float fRatioUsed = fDuration == 0.0 ? 1 : fTimeUsed / fDuration; - - float fDistance = fPriorSpeed - seg.m_fPercent; - float fRatioNeed = fRatioUsed * -fDistance; - return (fPriorSpeed + fRatioNeed); - } - else - { - return seg.m_fPercent; - } - -} - /* For visibility testing: if bAbsolute is false, random modifiers must return * the minimum possible scroll speed. */ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, float &fPeakYOffsetOut, bool &bIsPastPeakOut, bool bAbsolute ) @@ -277,7 +233,7 @@ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float { float fBeatsUntilStep = fNoteBeat - fSongBeat; float fYOffsetBeatSpacing = fBeatsUntilStep; - float fSpeedMultiplier = ( GAMESTATE->m_bInStepEditor || !GAMESTATE->m_bIsUsingStepTiming ) ? 1.0 : GetSpeedMultiplier( position.m_fSongBeatVisible, position.m_fMusicSecondsVisible, pCurSteps->m_Timing ); + float fSpeedMultiplier = ( GAMESTATE->m_bInStepEditor || !GAMESTATE->m_bIsUsingStepTiming ) ? 1.0 : pCurSteps->m_Timing.GetDisplayedSpeedPercent( position.m_fSongBeatVisible, position.m_fMusicSecondsVisible ); fYOffset += fSpeedMultiplier * fYOffsetBeatSpacing * (1-pPlayerState->m_PlayerOptions.GetCurrent().m_fTimeSpacing); } diff --git a/src/TimingData.cpp b/src/TimingData.cpp index 9e7f064cf1..cf8e3fb728 100644 --- a/src/TimingData.cpp +++ b/src/TimingData.cpp @@ -1341,6 +1341,50 @@ void TimingData::DeleteRows( int iStartRow, int iRowsToDelete ) this->SetBPMAtRow( iStartRow, fNewBPM ); } +float TimingData::GetDisplayedSpeedPercent( float fSongBeat, float fMusicSeconds ) +{ + if( m_SpeedSegments.size() == 0 ) + return 1.0; + + const int index = GetSpeedSegmentIndexAtBeat( fSongBeat ); + + const SpeedSegment &seg = m_SpeedSegments[index]; + float fStartBeat = NoteRowToBeat(seg.m_iStartRow); + float fStartTime = GetElapsedTimeFromBeat( fStartBeat ) - GetDelayAtBeat( fStartBeat ); + float fEndTime; + float fCurTime = fMusicSeconds; + + if( seg.m_usMode == 1 ) // seconds + { + fEndTime = fStartTime + seg.m_fWait; + } + else + { + fEndTime = GetElapsedTimeFromBeat( fStartBeat + seg.m_fWait ) - GetDelayAtBeat( fStartBeat + seg.m_fWait ); + } + + if( ( index == 0 && m_SpeedSegments[0].m_fWait > 0.0 ) && fCurTime < fStartTime ) + { + return 1.0; + } + else if( fEndTime >= fCurTime && ( index > 0 || m_SpeedSegments[0].m_fWait > 0.0 ) ) + { + const float fPriorSpeed = ( index == 0 ? 1 : m_SpeedSegments[index - 1].m_fPercent ); + float fTimeUsed = fCurTime - fStartTime; + float fDuration = fEndTime - fStartTime; + float fRatioUsed = fDuration == 0.0 ? 1 : fTimeUsed / fDuration; + + float fDistance = fPriorSpeed - seg.m_fPercent; + float fRatioNeed = fRatioUsed * -fDistance; + return (fPriorSpeed + fRatioNeed); + } + else + { + return seg.m_fPercent; + } + +} + void TimingData::TidyUpData() { // If there are no BPM segments, provide a default. diff --git a/src/TimingData.h b/src/TimingData.h index 21add1ba39..f64b9054c7 100644 --- a/src/TimingData.h +++ b/src/TimingData.h @@ -1594,6 +1594,8 @@ public: */ void AddSpeedSegment( const SpeedSegment &seg ); + float GetDisplayedSpeedPercent( float fBeat, float fMusicSeconds ); + /** * @brief Determine when the fakes end. * @param iRow The row you start on. From 5cde71754719723bc2af117ef08f14fb9870edc0 Mon Sep 17 00:00:00 2001 From: Thai Pangsakulyanont Date: Tue, 24 May 2011 12:27:03 +0700 Subject: [PATCH 03/12] GetDisplayedSpeedPercent should be const --- src/TimingData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TimingData.h b/src/TimingData.h index f64b9054c7..c44b4da58a 100644 --- a/src/TimingData.h +++ b/src/TimingData.h @@ -1594,7 +1594,7 @@ public: */ void AddSpeedSegment( const SpeedSegment &seg ); - float GetDisplayedSpeedPercent( float fBeat, float fMusicSeconds ); + float GetDisplayedSpeedPercent( float fBeat, float fMusicSeconds ) const; /** * @brief Determine when the fakes end. From 0533e742c5b6b1175722140eb5d1eb99ddd0e73c Mon Sep 17 00:00:00 2001 From: Thai Pangsakulyanont Date: Tue, 24 May 2011 12:29:55 +0700 Subject: [PATCH 04/12] a const is need on TimingData.cpp too --- src/TimingData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TimingData.cpp b/src/TimingData.cpp index cf8e3fb728..ced065bb8e 100644 --- a/src/TimingData.cpp +++ b/src/TimingData.cpp @@ -1341,7 +1341,7 @@ void TimingData::DeleteRows( int iStartRow, int iRowsToDelete ) this->SetBPMAtRow( iStartRow, fNewBPM ); } -float TimingData::GetDisplayedSpeedPercent( float fSongBeat, float fMusicSeconds ) +float TimingData::GetDisplayedSpeedPercent( float fSongBeat, float fMusicSeconds ) const { if( m_SpeedSegments.size() == 0 ) return 1.0; From 51229ad086756e61a2bddb1abc0f2dc1c6ca885f Mon Sep 17 00:00:00 2001 From: Thai Pangsakulyanont Date: Tue, 24 May 2011 12:44:48 +0700 Subject: [PATCH 05/12] limit searching to 16 beats --- src/NoteField.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/NoteField.cpp b/src/NoteField.cpp index b687c88ce6..4266675887 100644 --- a/src/NoteField.cpp +++ b/src/NoteField.cpp @@ -708,6 +708,7 @@ float FindLastDisplayedBeat( const PlayerState* pPlayerState, int iDrawDistanceB // Adjust search distance so that notes don't pop onto the screen. float fSearchDistance = 10; float fLastBeatToDraw = GetDisplayedPosition(pPlayerState)->m_fSongBeat+fSearchDistance; + float fSpeedMultiplier = GetDisplayedTiming(pPlayerState)->GetDisplayedSpeedPercent(GetDisplayedPosition(pPlayerState)->m_fSongBeatVisible, GetDisplayedPosition(pPlayerState)->m_fMusicSecondsVisible); const int NUM_ITERATIONS = 20; @@ -733,6 +734,11 @@ float FindLastDisplayedBeat( const PlayerState* pPlayerState, int iDrawDistanceB fSearchDistance /= 2; } + if( fSpeedMultiplier < 0.75 ) + { + fLastBeatToDraw = min(fLastBeatToDraw, GetDisplayedPosition(pPlayerState)->m_fSongBeat + 16); + } + return fLastBeatToDraw; } From ddbecd5de491b5baec142fc959f1146bae13b841 Mon Sep 17 00:00:00 2001 From: Thai Pangsakulyanont Date: Tue, 24 May 2011 13:07:59 +0700 Subject: [PATCH 06/12] add a comment about behavior of stops and delays in warpsadd a comment about behavior of stops and delays in warpsadd a comment about behavior of stops and delays in warpsadd a comment about behavior of stops and delays in warpsadd a comment about behavior of stops and delays in warpsadd a comment about behavior of stops and delays in warpsadd a comment about behavior of stops and delays in warpsadd a comment about behavior of stops and delays in warpsadd a comment about behavior of stops and delays in warps --- src/TimingData.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TimingData.cpp b/src/TimingData.cpp index ced065bb8e..24035ab690 100644 --- a/src/TimingData.cpp +++ b/src/TimingData.cpp @@ -515,6 +515,7 @@ bool TimingData::IsWarpAtRow( int iNoteRow ) const const WarpSegment& s = m_WarpSegments[i]; if( s.m_iStartRow <= iNoteRow && iNoteRow < (s.m_iStartRow + BeatToNoteRow(s.m_fLengthBeats) ) ) { + // Allow stops inside warps to allow things like stop, warp, stop, warp, stop, and so on. if( m_StopSegments.empty() ) { return true; From ccc66ccaeb8ab69d49ab6c1997971df0546be9eb Mon Sep 17 00:00:00 2001 From: Thai Pangsakulyanont Date: Tue, 24 May 2011 13:30:21 +0700 Subject: [PATCH 07/12] disable editing wait and mode on a row without a speed segment. enabling it is no-op anyway. --- src/ScreenEdit.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index 0c9faf76ae..65d41e3c54 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -3204,6 +3204,8 @@ void ScreenEdit::DisplayTimingMenu() { float fBeat = GetBeat(); TimingData &pTime = GetAppropriateTiming(); + bool bHasSpeedOnThisRow = pTime.GetSpeedSegmentAtBeat( fBeat ).m_iStartRow == BeatToNoteRow( fBeat ); + g_TimingDataInformation.rows[beat_0_offset].SetOneUnthemedChoice( ssprintf("%.5f", pTime.m_fBeat0OffsetInSeconds) ); g_TimingDataInformation.rows[bpm].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetBPMAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[stop].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetStopAtBeat( fBeat ) ) ) ; @@ -3213,8 +3215,8 @@ void ScreenEdit::DisplayTimingMenu() g_TimingDataInformation.rows[tickcount].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTickcountAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[combo].SetOneUnthemedChoice( ssprintf("%d", pTime.GetComboAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[warp].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetWarpAtBeat( fBeat ) ) ); - g_TimingDataInformation.rows[speed_percent].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetSpeedPercentAtBeat( fBeat ) ) ); - g_TimingDataInformation.rows[speed_wait].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetSpeedWaitAtBeat( fBeat ) ) ); + g_TimingDataInformation.rows[speed_percent].SetOneUnthemedChoice( bHasSpeedOnThisRow ? ssprintf("%.5f", pTime.GetSpeedPercentAtBeat( fBeat ) ) : "---" ); + g_TimingDataInformation.rows[speed_wait].SetOneUnthemedChoice( bHasSpeedOnThisRow ? ssprintf("%.5f", pTime.GetSpeedWaitAtBeat( fBeat ) ) : "---" ); RString starting = ( pTime.GetSpeedModeAtBeat( fBeat ) == 1 ? "Seconds" : "Beats" ); g_TimingDataInformation.rows[speed_mode].SetOneUnthemedChoice( starting.c_str() ); @@ -3224,8 +3226,8 @@ void ScreenEdit::DisplayTimingMenu() g_TimingDataInformation.rows[tickcount].bEnabled = GAMESTATE->m_bIsUsingStepTiming; g_TimingDataInformation.rows[combo].bEnabled = GAMESTATE->m_bIsUsingStepTiming; g_TimingDataInformation.rows[speed_percent].bEnabled = GAMESTATE->m_bIsUsingStepTiming; - g_TimingDataInformation.rows[speed_wait].bEnabled = GAMESTATE->m_bIsUsingStepTiming; - g_TimingDataInformation.rows[speed_mode].bEnabled = GAMESTATE->m_bIsUsingStepTiming; + g_TimingDataInformation.rows[speed_wait].bEnabled = GAMESTATE->m_bIsUsingStepTiming && bHasSpeedOnThisRow; + g_TimingDataInformation.rows[speed_mode].bEnabled = GAMESTATE->m_bIsUsingStepTiming && bHasSpeedOnThisRow; g_TimingDataInformation.rows[fake].bEnabled = GAMESTATE->m_bIsUsingStepTiming; EditMiniMenu( &g_TimingDataInformation, SM_BackFromTimingDataInformation ); From b2a5a68b4a765e6b177a6133fad0e2dc1b2d7e26 Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Tue, 24 May 2011 15:01:52 -0400 Subject: [PATCH 08/12] Super Shuffle works with fakes now. --- src/NoteDataUtil.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NoteDataUtil.cpp b/src/NoteDataUtil.cpp index 413fd3a425..c892d3d52b 100644 --- a/src/NoteDataUtil.cpp +++ b/src/NoteDataUtil.cpp @@ -1359,6 +1359,7 @@ static void SuperShuffleTaps( NoteData &inout, int iStartIndex, int iEndIndex ) case TapNote::mine: case TapNote::attack: case TapNote::lift: + case TapNote::fake: break; // ok to swap with this DEFAULT_FAIL( tn2.type ); } From 2afdaf6705d7e1ea11701c07eaf6429255b6ee4f Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Tue, 24 May 2011 21:15:59 -0400 Subject: [PATCH 09/12] Don't define a variable until it's needed. Still suspect there's an issue here, but can't identify what. --- src/Player.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Player.cpp b/src/Player.cpp index 98483728b7..c65d9ed1d4 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -2940,13 +2940,13 @@ void Player::RandomizeNotes( int iNoteRow ) int iNumOfTracks = m_NoteData.GetNumTracks(); for( int t=0; t+1 < iNumOfTracks; t++ ) { - const int iSwapWith = RandomInt( iNumOfTracks ); - /* Only swap a tap and an empty. */ NoteData::iterator iter = m_NoteData.FindTapNote( t, iNewNoteRow ); if( iter == m_NoteData.end(t) || iter->second.type != TapNote::tap ) continue; + const int iSwapWith = RandomInt( iNumOfTracks ); + // Make sure this is empty. if( m_NoteData.FindTapNote(iSwapWith, iNewNoteRow) != m_NoteData.end(iSwapWith) ) continue; From b3cebd2aafc32cda92612fbd192cc45df4a91579 Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Tue, 24 May 2011 21:29:51 -0400 Subject: [PATCH 10/12] No need to swap if it's the same column. Still busted, but still stumped. Doing what I can to optimize. --- src/Player.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Player.cpp b/src/Player.cpp index c65d9ed1d4..6ebcdfac8b 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -2947,6 +2947,10 @@ void Player::RandomizeNotes( int iNoteRow ) const int iSwapWith = RandomInt( iNumOfTracks ); + // Make sure we're not swapping with ourselves. + if( t == iSwapWith ) + continue; + // Make sure this is empty. if( m_NoteData.FindTapNote(iSwapWith, iNewNoteRow) != m_NoteData.end(iSwapWith) ) continue; From 8d2f1017e123140bacfdab239ab3986408e03407 Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Tue, 24 May 2011 22:13:22 -0400 Subject: [PATCH 11/12] Enforce validation on TapNote types. --- src/NoteTypes.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/NoteTypes.h b/src/NoteTypes.h index b842dd2f16..a91e6aa693 100644 --- a/src/NoteTypes.h +++ b/src/NoteTypes.h @@ -5,6 +5,7 @@ #include "GameConstantsAndTypes.h" #include "PlayerNumber.h" +#include "RageLog.h" class XNode; @@ -159,7 +160,14 @@ struct TapNote pn(PLAYER_INVALID), bHopoPossible(false), sAttackModifiers(sAttackModifiers_), fAttackDurationSeconds(fAttackDurationSeconds_), - iKeysoundIndex(iKeysoundIndex_), iDuration(0), HoldResult() {} + iKeysoundIndex(iKeysoundIndex_), iDuration(0), HoldResult() + { + if (type_ > TapNote::fake ) + { + LOG->Trace(ssprintf("Invalid tap note type %d (most likely) due to random vanish issues. Assume it doesn't need judging.", type_ ) ); + type = TapNote::empty; + } + } /** * @brief Determine if the two TapNotes are equal to each other. From c5bbeb69cae95a07f70845a1423f48414c97a50c Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Tue, 24 May 2011 22:14:59 -0400 Subject: [PATCH 12/12] Empty notes don't get judged (RandomVanish). --- src/Player.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Player.cpp b/src/Player.cpp index 6ebcdfac8b..34d446e620 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -474,6 +474,7 @@ static bool NeedsTapJudging( const TapNote &tn ) case TapNote::attack: case TapNote::autoKeysound: case TapNote::fake: + case TapNote::empty: return false; } } @@ -496,6 +497,7 @@ static bool NeedsHoldJudging( const TapNote &tn ) case TapNote::attack: case TapNote::autoKeysound: case TapNote::fake: + case TapNote::empty: return false; } }