From b3056eee1c1e9a6c319d3abdb7847e92022407d3 Mon Sep 17 00:00:00 2001 From: Steve Checkoway Date: Fri, 28 Jul 2006 09:05:58 +0000 Subject: [PATCH] Fix mines not being triggered when being held down (for the most part). It's still possible for mines to not be triggered if the input released after the mine has passed but before the next Update() frame. This is much tricker to deal with since we can't ask if the button was held down at some time in the past It is also much harder to detect than having held down the button for an entire measure and watched mines just not be hit. This should also fix autoplay misses by telling Step() exactly which row was pressed. There are also comments in there about possible bugs in the grading/key sounds but I didn't want to change those without getting input from other devs since it does change the way we grade "jack hammers." --- stepmania/src/Player.cpp | 67 +++++++++++++++++++------------- stepmania/src/Player.h | 2 +- stepmania/src/ScreenEdit.cpp | 2 +- stepmania/src/ScreenGameplay.cpp | 4 +- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/stepmania/src/Player.cpp b/stepmania/src/Player.cpp index e4133838a4..9a29ef08eb 100644 --- a/stepmania/src/Player.cpp +++ b/stepmania/src/Player.cpp @@ -571,7 +571,7 @@ void Player::Update( float fDeltaTime ) if( tn.HoldResult.fLife >= 0.5f ) continue; - Step( iTrack, now, false ); + Step( iTrack, iSongRow, now, false ); if( m_pPlayerState->m_PlayerController == PC_AUTOPLAY ) STATSMAN->m_CurStageStats.bUsedAutoplay = true; } @@ -922,30 +922,25 @@ int Player::GetClosestNote( int col, int iNoteRow, int iMaxRowsAhead, int iMaxRo return iNextIndex; } -void Player::Step( int col, const RageTimer &tm, bool bHeld ) +void Player::Step( int col, int row, const RageTimer &tm, bool bHeld ) { - bool bOniDead = - GAMESTATE->m_SongOptions.m_LifeType == SongOptions::LIFE_BATTERY && - m_pPlayerStageStats && - m_pPlayerStageStats->bFailed; - if( bOniDead ) - return; // do nothing + // If we're playing on oni and we've died, do nothing. + if( GAMESTATE->m_SongOptions.m_LifeType == SongOptions::LIFE_BATTERY && m_pPlayerStageStats && m_pPlayerStageStats->bFailed ) + return; DEBUG_ASSERT_M( col >= 0 && col <= m_NoteData.GetNumTracks(), ssprintf("%i, %i", col, m_NoteData.GetNumTracks()) ); - float fPositionSeconds = GAMESTATE->m_fMusicSeconds; - fPositionSeconds -= tm.Ago(); + const float fPositionSeconds = GAMESTATE->m_fMusicSeconds - tm.Ago(); const float fSongBeat = GAMESTATE->m_pCurSong ? GAMESTATE->m_pCurSong->GetBeatFromElapsedTime( fPositionSeconds ) : GAMESTATE->m_fSongBeat; - - + const int iSongRow = row == -1 ? BeatToNoteRow( fSongBeat ) : row; + { // Update roll life // Let's not check the whole array every time. // Instead, only check 1 beat back. Even 1 is overkill. // Just update the life here and let Update judge the roll. - const int iSongRow = BeatToNoteRow( fSongBeat ); const int iStartCheckingAt = max( 0, iSongRow-BeatToNoteRow(1) ); NoteData::iterator begin, end; m_NoteData.GetTapNoteRangeInclusive( col, iStartCheckingAt, iSongRow+1, begin, end ); @@ -954,7 +949,7 @@ void Player::Step( int col, const RageTimer &tm, bool bHeld ) TapNote &tn = begin->second; if( tn.type != TapNote::hold_head ) continue; - int iRow = begin->first; + const int iRow = begin->first; HoldNoteScore hns = tn.HoldResult.hns; if( hns != HNS_None ) // if this HoldNote already has a result @@ -963,8 +958,7 @@ void Player::Step( int col, const RageTimer &tm, bool bHeld ) // if they got a bad score or haven't stepped on the corresponding tap yet const TapNoteScore tns = tn.result.tns; const bool bSteppedOnTapNote = tns != TNS_None && tns != TNS_Miss; // did they step on the start of this roll? - - int iEndRow = iRow + tn.iDuration; + const int iEndRow = iRow + tn.iDuration; if( bSteppedOnTapNote && tn.HoldResult.fLife != 0 ) { @@ -1010,7 +1004,7 @@ void Player::Step( int col, const RageTimer &tm, bool bHeld ) { // TODO: remove use of PlayerNumber PlayerNumber pn = m_pPlayerState->m_PlayerNumber; - Profile* pProfile = PROFILEMAN->GetProfile(pn); + Profile *pProfile = PROFILEMAN->GetProfile( pn ); if( pProfile ) { int iNumTracksHeld = 0; @@ -1018,7 +1012,7 @@ void Player::Step( int col, const RageTimer &tm, bool bHeld ) { const StyleInput StyleI( pn, t ); const GameInput GameI = GAMESTATE->GetCurrentStyle()->StyleInputToGameInput( StyleI ); - float fSecsHeld = INPUTMAPPER->GetSecsHeld( GameI ); + const float fSecsHeld = INPUTMAPPER->GetSecsHeld( GameI ); if( fSecsHeld > 0 && fSecsHeld < JUMP_WINDOW_SECONDS ) iNumTracksHeld++; } @@ -1049,8 +1043,16 @@ void Player::Step( int col, const RageTimer &tm, bool bHeld ) // // Check for step on a TapNote // + /* XXX: This seems wrong. If a player steps twice quickly and two notes are close together in the same column then + * it is possible for the two notes to be graded out of order. Two possible fixes: + * 1. Adjust the fSongBeat (or the resulting note row) backward by iStepSearchRows and search forward two iStepSearchRows + * lengths, disallowing graded. This doesn't seem right because if a second note has passed, an earlier one should not + * be graded. + * 2. Clamp the distance searched backward to the previous row graded. + * Either option would fundamentally change the grading of two quick notes "jack hammers." Hmm. + */ const int iStepSearchRows = BeatToNoteRow( StepSearchDistance * GAMESTATE->m_fCurBPS * GAMESTATE->m_SongOptions.m_fMusicRate ); - int iIndexOverlappingNote = GetClosestNote( col, BeatToNoteRow(fSongBeat), iStepSearchRows, iStepSearchRows, false ); + int iIndexOverlappingNote = row == -1 ? GetClosestNote( col, BeatToNoteRow(fSongBeat), iStepSearchRows, iStepSearchRows, false ) : row; // calculate TapNoteScore TapNoteScore score = TNS_None; @@ -1058,7 +1060,9 @@ void Player::Step( int col, const RageTimer &tm, bool bHeld ) if( iIndexOverlappingNote != -1 ) { // compute the score for this hit - float fNoteOffset; + float fNoteOffset = 0.0f; + + if( row != -1 ) { const float fStepBeat = NoteRowToBeat( iIndexOverlappingNote ); const float fStepSeconds = GAMESTATE->m_pCurSong->GetElapsedTimeFromBeat(fStepBeat); @@ -1082,8 +1086,10 @@ void Player::Step( int col, const RageTimer &tm, bool bHeld ) const float fSecondsFromExact = fabsf( fNoteOffset ); - - TapNote &tn = m_NoteData.FindTapNote( col, iIndexOverlappingNote )->second; + NoteData::iterator iter = m_NoteData.FindTapNote( col, iIndexOverlappingNote ); + + DEBUG_ASSERT( iter!= m_NoteData.end(col) ); + TapNote &tn = iter->second; switch( m_pPlayerState->m_PlayerController ) { @@ -1116,6 +1122,8 @@ void Player::Step( int col, const RageTimer &tm, bool bHeld ) case PC_AUTOPLAY: score = PlayerAI::GetTapNoteScore( m_pPlayerState ); + /* XXX: This doesn't make sense. Step should only be called in autoplay for hit notes */ +#if 0 // GetTapNoteScore always returns TNS_W1 in autoplay. // If the step is far away, don't judge it. if( m_pPlayerState->m_PlayerController == PC_AUTOPLAY && @@ -1124,6 +1132,7 @@ void Player::Step( int col, const RageTimer &tm, bool bHeld ) score = TNS_None; break; } +#endif // TRICKY: We're asking the AI to judge mines. consider TNS_W4 and below // as "mine was hit" and everything else as "mine was avoided" @@ -1160,7 +1169,7 @@ void Player::Step( int col, const RageTimer &tm, bool bHeld ) return; // Put some small, random amount in fNoteOffset so that demonstration - // show a mix of late and early instead of always late. + // show a mix of late and early. fNoteOffset = randomf( -0.1f, 0.1f ); break; @@ -1246,6 +1255,12 @@ void Player::Step( int col, const RageTimer &tm, bool bHeld ) { /* Search for keyed sounds separately. If we can't find a nearby note, search * backwards indefinitely, and ignore grading. */ + /* XXX: This isn't quite right. As per the above XXX for iIndexOverlappingNote, if iIndexOverlappingNote + * is set to a previous note, the keysound could have changed and this would cause the wrong one to play, + * in essence playing two sounds in the opposite order. Maybe this should always perform the search. Still, + * even that doesn't seem quite right since it would then play the same (new) keysound twice which would + * sound wrong even though the notes were judged as being correct, above. Fixing the above problem would + * fix this one as well. */ if( iIndexOverlappingNote == -1 ) iIndexOverlappingNote = GetClosestNote( col, BeatToNoteRow(fSongBeat), iStepSearchRows, MAX_NOTE_ROW, true ); @@ -1448,7 +1463,7 @@ void Player::CrossedRow( int iNoteRow, const RageTimer &now ) const TapNote &tn = m_NoteData.GetTapNote( t, iNoteRow ); if( tn.type != TapNote::empty && tn.result.tns == TNS_None ) { - Step( t, now ); + Step( t, iNoteRow, now ); if( m_pPlayerState->m_PlayerController == PC_AUTOPLAY ) STATSMAN->m_CurStageStats.bUsedAutoplay = true; } @@ -1471,13 +1486,13 @@ void Player::CrossedMineRow( int iNoteRow, const RageTimer &now ) { float fSecsHeld = INPUTMAPPER->GetSecsHeld( StyleI, m_pPlayerState->m_mp ); if( fSecsHeld >= PREFSMAN->m_fPadStickSeconds ) - Step( t, now+(-PREFSMAN->m_fPadStickSeconds), true ); + Step( t, iNoteRow, now+(-PREFSMAN->m_fPadStickSeconds), true ); } else { bool bIsDown = INPUTMAPPER->IsButtonDown( StyleI, m_pPlayerState->m_mp ); if( bIsDown ) - Step( t, now, true ); + Step( t, iNoteRow, now, true ); } } } diff --git a/stepmania/src/Player.h b/stepmania/src/Player.h index 93ce141ce5..7885780c40 100644 --- a/stepmania/src/Player.h +++ b/stepmania/src/Player.h @@ -111,7 +111,7 @@ public: void Load(); void CrossedRow( int iNoteRow, const RageTimer &now ); void CrossedMineRow( int iNoteRow, const RageTimer &now ); - void Step( int col, const RageTimer &tm, bool bHeld = false ); + void Step( int col, int row, const RageTimer &tm, bool bHeld = false ); void RandomizeNotes( int iNoteRow ); void FadeToFail(); void CacheAllUsedNoteSkins(); diff --git a/stepmania/src/ScreenEdit.cpp b/stepmania/src/ScreenEdit.cpp index 44b44840a8..5b26e332a5 100644 --- a/stepmania/src/ScreenEdit.cpp +++ b/stepmania/src/ScreenEdit.cpp @@ -2035,7 +2035,7 @@ void ScreenEdit::InputPlay( const InputEventPlus &input, EditButton EditB ) { case PLAYER_1: if( PREFSMAN->m_AutoPlay == PC_HUMAN ) - m_Player->Step( input.StyleI.col, input.DeviceI.ts ); + m_Player->Step( input.StyleI.col, -1, input.DeviceI.ts ); break; } diff --git a/stepmania/src/ScreenGameplay.cpp b/stepmania/src/ScreenGameplay.cpp index 56ce257b2e..f146608839 100644 --- a/stepmania/src/ScreenGameplay.cpp +++ b/stepmania/src/ScreenGameplay.cpp @@ -2168,7 +2168,7 @@ void ScreenGameplay::Input( const InputEventPlus &input ) input.StyleI.IsValid() && GAMESTATE->IsMultiPlayerEnabled(input.mp) ) { - m_vPlayerInfo[input.mp].m_pPlayer->Step( input.StyleI.col, input.DeviceI.ts ); + m_vPlayerInfo[input.mp].m_pPlayer->Step( input.StyleI.col, -1, input.DeviceI.ts ); } } else @@ -2185,7 +2185,7 @@ void ScreenGameplay::Input( const InputEventPlus &input ) if( PREFSMAN->m_AutoPlay == PC_HUMAN ) { PlayerInfo& pi = GetPlayerInfoForInput( input ); - pi.m_pPlayer->Step( input.StyleI.col, input.DeviceI.ts ); + pi.m_pPlayer->Step( input.StyleI.col, -1, input.DeviceI.ts ); } } }