diff --git a/stepmania/src/ArrowEffects.cpp b/stepmania/src/ArrowEffects.cpp index a8bf834530..b03ad807db 100644 --- a/stepmania/src/ArrowEffects.cpp +++ b/stepmania/src/ArrowEffects.cpp @@ -25,8 +25,13 @@ static float GetNoteFieldHeight( const PlayerState* pPlayerState ) /* 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, bool bAbsolute ) +float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, float &fPeakYOffsetOut, bool &bIsPastPeakOut, bool bAbsolute ) { + // Default values that are returned if boomerang is off. + fPeakYOffsetOut = FLT_MAX; + bIsPastPeakOut = true; + + float fYOffset = 0; /* Usually, fTimeSpacing is 0 or 1, in which case we use entirely beat spacing or @@ -84,9 +89,22 @@ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float fYOffset += fYAdjust; + // + // Factor in boomerang + // if( fAccels[PlayerOptions::ACCEL_BOOMERANG] > 0 ) - fYOffset += fAccels[PlayerOptions::ACCEL_BOOMERANG] * (fYOffset * SCALE( fYOffset, 0.f, SCREEN_HEIGHT, 1.5f, 0.5f )- fYOffset); + { + float fOriginalYOffset = fYOffset; + fYOffset = (-1*fOriginalYOffset*fOriginalYOffset/SCREEN_HEIGHT) + 1.5*fOriginalYOffset; + float fPeakAtYOffset = SCREEN_HEIGHT * 0.75; // zero point of function above + fPeakYOffsetOut = (-1*fPeakAtYOffset*fPeakAtYOffset/SCREEN_HEIGHT) + 1.5*fPeakAtYOffset; + bIsPastPeakOut = fOriginalYOffset < fPeakAtYOffset; + } + + // + // Factor in scroll speed + // float fScrollSpeed = pPlayerState->m_CurrentPlayerOptions.m_fScrollSpeed; if( pPlayerState->m_CurrentPlayerOptions.m_fRandomSpeed > 0 && !bAbsolute ) { @@ -119,11 +137,11 @@ float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float } fYOffset *= fScrollSpeed; + fPeakYOffsetOut *= fScrollSpeed; return fYOffset; } - void ArrowGetReverseShiftAndScale( const PlayerState* pPlayerState, int iCol, float fYReverseOffsetPixels, float &fShiftOut, float &fScaleOut ) { /* XXX: Hack: we need to scale the reverse shift by the zoom. */ diff --git a/stepmania/src/ArrowEffects.h b/stepmania/src/ArrowEffects.h index 2a277a9be2..fb0c1143ed 100644 --- a/stepmania/src/ArrowEffects.h +++ b/stepmania/src/ArrowEffects.h @@ -12,7 +12,13 @@ public: // fYOffset is a vertical position in pixels relative to the center. // (positive if has not yet been stepped on, negative if has already passed). // The ArrowEffect and ScrollSpeed is applied in this stage. - static float GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, bool bAbsolute=false ); + static float GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, float &fPeakYOffsetOut, bool &bIsPastPeakYOffset, bool bAbsolute=false ); + static float GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, bool bAbsolute=false ) + { + float fThrowAway; + bool bThrowAway; + return GetYOffset( pPlayerState, iCol, fNoteBeat, fThrowAway, bThrowAway, bAbsolute ); + } /* Actual display position, with reverse and post-reverse-effects factored in * (fYOffset -> YPos). */ diff --git a/stepmania/src/NoteDisplay.cpp b/stepmania/src/NoteDisplay.cpp index 3114644b01..ab0eaf6d60 100644 --- a/stepmania/src/NoteDisplay.cpp +++ b/stepmania/src/NoteDisplay.cpp @@ -986,20 +986,34 @@ void NoteDisplay::DrawHoldHead( const TapNote& tn, int iCol, int iRow, const boo void NoteDisplay::DrawHold( const TapNote &tn, int iCol, int iRow, bool bIsBeingHeld, bool bIsActive, const HoldNoteResult &Result, float fPercentFadeToFail, bool bDrawGlowOnly, float fReverseOffsetPixels, float fYStartOffset, float fYEndOffset ) { - int iEndBeat = iRow + tn.iDuration; + int iEndRow = iRow + tn.iDuration; // bDrawGlowOnly is a little hacky. We need to draw the diffuse part and the glow part one pass at a time to minimize state changes bool bReverse = m_pPlayerState->m_CurrentPlayerOptions.GetReversePercentForColumn(iCol) > 0.5f; float fStartBeat = NoteRowToBeat( max(tn.HoldResult.iLastHeldRow, iRow) ); - float fStartYOffset = ArrowEffects::GetYOffset( m_pPlayerState, iCol, fStartBeat ); - - // HACK: If active, don't allow the top of the hold to go above the receptor - if( bIsActive ) - fStartYOffset = 0; + float fThrowAway = 0; + // HACK: If active, don't set YOffset to 0 so that it doesn't jiggle around the receptor. + bool bStartIsPastPeak = true; + float fStartYOffset = 0; + if( bIsActive ) + ; // use the default values filled in above + else + fStartYOffset = ArrowEffects::GetYOffset( m_pPlayerState, iCol, fStartBeat, fThrowAway, bStartIsPastPeak ); + float fStartYPos = ArrowEffects::GetYPos( m_pPlayerState, iCol, fStartYOffset, fReverseOffsetPixels ); - float fEndYOffset = ArrowEffects::GetYOffset( m_pPlayerState, iCol, NoteRowToBeat(iEndBeat) ); + float fEndPeakYOffset = 0; + bool bEndIsPastPeak = false; + float fEndYOffset = ArrowEffects::GetYOffset( m_pPlayerState, iCol, NoteRowToBeat(iEndRow), fEndPeakYOffset, bEndIsPastPeak ); + + // In boomerang, the arrows reverse direction at Y offset value fPeakAtYOffset. + // If fPeakAtYOffset lies inside of the hold we're drawing, then the we + // want to draw the tail at that max Y offset, or else the hold will appear + // to magically grow as the tail approaches the max Y offset. + if( bStartIsPastPeak && !bEndIsPastPeak ) + fEndYOffset = fEndPeakYOffset; // use the calculated PeakYOffset so that long holds don't appear to grow + float fEndYPos = ArrowEffects::GetYPos( m_pPlayerState, iCol, fEndYOffset, fReverseOffsetPixels ); const float fYHead = bReverse ? fEndYPos : fStartYPos; // the center of the head diff --git a/stepmania/src/NoteField.cpp b/stepmania/src/NoteField.cpp index 8b136a5209..4a2cf780b2 100644 --- a/stepmania/src/NoteField.cpp +++ b/stepmania/src/NoteField.cpp @@ -572,12 +572,18 @@ void NoteField::DrawPrimitives() // TRICKY: If boomerang is on, then all notes in the range // [iFirstIndexToDraw,iLastIndexToDraw] aren't necessarily visible. // Test every note to make sure it's on screen before drawing - float fYStartOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(iStartRow) ); - float fYEndOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(iEndRow) ); - if( !( iFirstPixelToDraw <= fYEndOffset && fYEndOffset <= iLastPixelToDraw || - iFirstPixelToDraw <= fYStartOffset && fYStartOffset <= iLastPixelToDraw || - fYStartOffset < iFirstPixelToDraw && fYEndOffset > iLastPixelToDraw ) ) + float fThrowAway; + bool bStartIsPastPeak = false; + bool bEndIsPastPeak = false; + float fStartYOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(iStartRow), fThrowAway, bStartIsPastPeak ); + float fEndYOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToBeat(iEndRow), fThrowAway, bEndIsPastPeak ); + + bool bTailIsOnVisible = iFirstPixelToDraw <= fEndYOffset && fEndYOffset <= iLastPixelToDraw; + bool bHeadIsVisible = iFirstPixelToDraw <= fStartYOffset && fStartYOffset <= iLastPixelToDraw; + bool bStraddlingVisible = bStartIsPastPeak && !bEndIsPastPeak; + if( !(bTailIsOnVisible || bHeadIsVisible || bStraddlingVisible) ) { + //LOG->Trace( "skip drawing this hold." ); continue; // skip }