diff --git a/src/ArrowEffects.cpp b/src/ArrowEffects.cpp index 9a615caacd..b295d67157 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 ) 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 ); } diff --git a/src/NoteField.cpp b/src/NoteField.cpp index 06917ac2be..fb1ac42670 100644 --- a/src/NoteField.cpp +++ b/src/NoteField.cpp @@ -728,6 +728,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; @@ -753,6 +754,11 @@ float FindLastDisplayedBeat( const PlayerState* pPlayerState, int iDrawDistanceB fSearchDistance /= 2; } + if( fSpeedMultiplier < 0.75 ) + { + fLastBeatToDraw = min(fLastBeatToDraw, GetDisplayedPosition(pPlayerState)->m_fSongBeat + 16); + } + return fLastBeatToDraw; } 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. 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" ); diff --git a/src/Player.cpp b/src/Player.cpp index 98483728b7..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; } } @@ -2940,13 +2942,17 @@ 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 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; diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index 365caacbc3..045083a529 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -3212,6 +3212,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 ) ) ) ; @@ -3221,8 +3223,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() ); @@ -3233,8 +3235,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 ); diff --git a/src/TimingData.cpp b/src/TimingData.cpp index 951a247494..1a8c0e9802 100644 --- a/src/TimingData.cpp +++ b/src/TimingData.cpp @@ -553,6 +553,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; @@ -1408,6 +1409,50 @@ void TimingData::DeleteRows( int iStartRow, int iRowsToDelete ) this->SetBPMAtRow( iStartRow, fNewBPM ); } +float TimingData::GetDisplayedSpeedPercent( float fSongBeat, float fMusicSeconds ) const +{ + 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 9314b74352..e65bcfbf16 100644 --- a/src/TimingData.h +++ b/src/TimingData.h @@ -1670,6 +1670,8 @@ public: */ void AddSpeedSegment( const SpeedSegment &seg ); + float GetDisplayedSpeedPercent( float fBeat, float fMusicSeconds ) const; + /** * @brief Retrieve the scrolling factor at the given row. * @param iNoteRow the row in question.