From af9b823b95237204bdec16128f830ce68a1463bd Mon Sep 17 00:00:00 2001 From: Chris Danford Date: Tue, 13 Mar 2007 06:00:08 +0000 Subject: [PATCH] experimental: combo checkpoints for holds --- stepmania/Themes/default/metrics.ini | 1 + stepmania/src/Player.cpp | 117 ++++++++++++++++++++++----- stepmania/src/Player.h | 6 +- 3 files changed, 101 insertions(+), 23 deletions(-) diff --git a/stepmania/Themes/default/metrics.ini b/stepmania/Themes/default/metrics.ini index a557515880..1f1598c22d 100644 --- a/stepmania/Themes/default/metrics.ini +++ b/stepmania/Themes/default/metrics.ini @@ -2232,6 +2232,7 @@ TapJudgmentsUnderField=false HoldJudgmentsUnderField=false PenalizeTapScoreNone=false JudgeHoldNotesOnSameRowTogether=false +HoldCheckpoints=false [PlayerShared] Fallback="Player" diff --git a/stepmania/src/Player.cpp b/stepmania/src/Player.cpp index 7400180771..827897f96c 100644 --- a/stepmania/src/Player.cpp +++ b/stepmania/src/Player.cpp @@ -128,6 +128,7 @@ Preference g_fTimingWindowHopo ( "TimingWindowHopo", 0.25 ); // max ti Preference g_fTimingWindowStrum ( "TimingWindowStrum", 0.1f ); // max time between strum and when the frets must match ThemeMetric PENALIZE_TAP_SCORE_NONE ( "Player", "PenalizeTapScoreNone" ); ThemeMetric JUDGE_HOLD_NOTES_ON_SAME_ROW_TOGETHER ( "Player", "JudgeHoldNotesOnSameRowTogether" ); +ThemeMetric HOLD_CHECKPOINTS ( "Player", "HoldCheckpoints" ); float Player::GetWindowSeconds( TimingWindow tw ) @@ -369,8 +370,8 @@ void Player::Load() m_LastTapNoteScore = TNS_None; // The editor can start playing in the middle of the song. const int iNoteRow = BeatToNoteRowNotRounded( GAMESTATE->m_fSongBeat ); - m_iRowLastCrossed = iNoteRow - 1; - m_iMineRowLastCrossed = iNoteRow - 1; + m_iFirstUncrossedRow = iNoteRow - 1; + m_iFirstUncrossedMineRow = iNoteRow - 1; m_iRowLastJudged = iNoteRow - 1; m_iMineRowLastJudged = iNoteRow - 1; m_pJudgedRows->Reset( iNoteRow ); @@ -708,11 +709,9 @@ void Player::Update( float fDeltaTime ) const int iRowNow = BeatToNoteRowNotRounded( GAMESTATE->m_fSongBeat ); if( iRowNow >= 0 ) { - // for each index we crossed since the last update if( GAMESTATE->IsPlayerEnabled(m_pPlayerState) ) - FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( m_NoteData, r, m_iRowLastCrossed, iRowNow+1 ) - CrossedRow( r, now ); - m_iRowLastCrossed = iRowNow+1; + CrossedRows( m_iFirstUncrossedRow, iRowNow, now ); + m_iFirstUncrossedRow = iRowNow+1; } } @@ -726,9 +725,9 @@ void Player::Update( float fDeltaTime ) { // for each index we crossed since the last update if( GAMESTATE->IsPlayerEnabled(m_pPlayerState) ) - FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( m_NoteData, r, m_iMineRowLastCrossed, iRowNow+1 ) + FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( m_NoteData, r, m_iFirstUncrossedMineRow, iRowNow+1 ) CrossedMineRow( r, now ); - m_iMineRowLastCrossed = iRowNow+1; + m_iFirstUncrossedMineRow = iRowNow+1; } } @@ -2166,23 +2165,101 @@ void Player::FlashGhostRow( int iRow, PlayerNumber pn ) } } -void Player::CrossedRow( int iNoteRow, const RageTimer &now ) +void Player::CrossedRows( int iFirstRowCrossed, int iLastRowCrossed, const RageTimer &now ) { - // If we're doing random vanish, randomise notes on the fly. - if( m_pPlayerState->m_PlayerOptions.GetCurrent().m_fAppearances[PlayerOptions::APPEARANCE_RANDOMVANISH]==1 ) - RandomizeNotes( iNoteRow ); + //LOG->Trace( "Player::CrossedRows %d %d", iFirstRowCrossed, iLastRowCrossed ); - // check to see if there's a note at the crossed row - if( m_pPlayerState->m_PlayerController != PC_HUMAN ) + // for each index we crossed since the last update + FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( m_NoteData, r, m_iFirstUncrossedRow, iLastRowCrossed+1 ) { - for( int t=0; tm_PlayerOptions.GetCurrent().m_fAppearances[PlayerOptions::APPEARANCE_RANDOMVANISH]==1 ) + RandomizeNotes( r ); + + // check to see if there's a note at the crossed row + if( m_pPlayerState->m_PlayerController != PC_HUMAN ) { - const TapNote &tn = m_NoteData.GetTapNote( t, iNoteRow ); - if( tn.type != TapNote::empty && tn.result.tns == TNS_None ) + for( int t=0; tm_PlayerController == PC_AUTOPLAY ) - STATSMAN->m_CurStageStats.m_bUsedAutoplay = true; + const TapNote &tn = m_NoteData.GetTapNote( t, r ); + if( tn.type != TapNote::empty && tn.result.tns == TNS_None ) + { + Step( t, r, now, false, false ); + if( m_pPlayerState->m_PlayerController == PC_AUTOPLAY ) + STATSMAN->m_CurStageStats.m_bUsedAutoplay = true; + } + } + } + } + + + // + // Update hold checkpoints + // + + if( HOLD_CHECKPOINTS ) + { + int iFirstCheckpointInRange = ((iFirstRowCrossed+ROWS_PER_BEAT-1)/ROWS_PER_BEAT) * ROWS_PER_BEAT; + int iLastCheckpointInRange = ((iLastRowCrossed)/ROWS_PER_BEAT) * ROWS_PER_BEAT; + + for( int r = iFirstCheckpointInRange; r <= iLastCheckpointInRange; r += ROWS_PER_BEAT ) + { + LOG->Trace( "%d...", r ); + bool bAnyHoldsThisRow = false; + bool bAllHoldsHeldThisRow = true; + + // start at r-1 so that we can count the tail of a hold as a checkpoint + NoteData::all_tracks_iterator iter = m_NoteData.GetTapNoteRangeAllTracks( r-1, r+1, NULL, true ); + for( ; !iter.IsAtEnd(); ++iter ) + { + TapNote &tn = *iter; + if( tn.type != TapNote::hold_head ) + continue; + + int iStartRow = iter.Row(); + int iEndRow = iStartRow + tn.iDuration; + + // First checkpoint is the first row after the hold head that lands on a beat. + int iFirstCheckpointOfHold = ((iStartRow+ROWS_PER_BEAT)/ROWS_PER_BEAT) * ROWS_PER_BEAT; + + // The last checkpoint is the end row or the first earlier row that lands on a beat. + int iLastCheckpointOfHold = ((iEndRow)/ROWS_PER_BEAT) * ROWS_PER_BEAT; + + // count the end of the hold as a checkpoint + + bool bHoldOverlapsRow = iFirstCheckpointOfHold <= r && r <= iLastCheckpointOfHold; + if( !bHoldOverlapsRow ) + continue; + + bAnyHoldsThisRow = true; + bAllHoldsHeldThisRow &= tn.HoldResult.bHeld; + } + + if( bAnyHoldsThisRow ) + { + if( m_pPlayerStageStats ) + { + // increment combo + const int iOldCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0; + const int iOldMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0; + + if( bAnyHoldsThisRow ) + { + m_pPlayerStageStats->m_iCurCombo++; + m_pPlayerStageStats->m_iCurMissCombo = 0; + } + else + { + m_pPlayerStageStats->m_iCurCombo = 0; + m_pPlayerStageStats->m_iCurMissCombo++; + } + + SendComboMessages( iOldCombo, iOldMissCombo ); + if( m_pPlayerStageStats ) + { + SetCombo( m_pPlayerStageStats->m_iCurCombo, m_pPlayerStageStats->m_iCurMissCombo ); + } + } } } } diff --git a/stepmania/src/Player.h b/stepmania/src/Player.h index 917e4acb54..86aad2cda7 100644 --- a/stepmania/src/Player.h +++ b/stepmania/src/Player.h @@ -65,7 +65,7 @@ public: ScoreKeeper* pPrimaryScoreKeeper, ScoreKeeper* pSecondaryScoreKeeper ); void Load(); - void CrossedRow( int iNoteRow, const RageTimer &now ); + void CrossedRows( int iFirstRowCrossed, int iLastRowCrossed, const RageTimer &now ); void CrossedMineRow( int iNoteRow, const RageTimer &now ); bool IsOniDead() const; @@ -167,8 +167,8 @@ protected: ScoreKeeper *m_pSecondaryScoreKeeper; Inventory *m_pInventory; - int m_iRowLastCrossed; - int m_iMineRowLastCrossed; + int m_iFirstUncrossedRow; + int m_iFirstUncrossedMineRow; int m_iRowLastJudged; // Everything up to and including this row has been judged. int m_iMineRowLastJudged; int m_iLastSeenCombo;