add a strum window: After pressing the strum button, the player has some time to hit matching frets before being scored TNS_None

This commit is contained in:
Chris Danford
2007-01-17 12:02:56 +00:00
parent 56ca4a5a18
commit c928ad75fa
4 changed files with 148 additions and 71 deletions
+133 -68
View File
@@ -71,7 +71,8 @@ static Preference<float> m_fTimingWindowScale ( "TimingWindowScale", 1.0f );
static Preference<float> m_fTimingWindowAdd ( "TimingWindowAdd", 0 ); static Preference<float> m_fTimingWindowAdd ( "TimingWindowAdd", 0 );
static Preference1D<float> m_fTimingWindowSeconds( TimingWindowSecondsInit, NUM_TimingWindow ); static Preference1D<float> m_fTimingWindowSeconds( TimingWindowSecondsInit, NUM_TimingWindow );
static Preference<float> m_fTimingWindowJump ( "TimingWindowJump", 0.25 ); static Preference<float> m_fTimingWindowJump ( "TimingWindowJump", 0.25 );
Preference<float> g_fTimingWindowHopo ( "TimingWindowHopo", 0.25 ); Preference<float> g_fTimingWindowHopo ( "TimingWindowHopo", 0.25 ); // max time between notes in a hopo chain
Preference<float> g_fTimingWindowStrum ( "TimingWindowStrum", 0.1f ); // max time between strum and when the frets must match
ThemeMetric<bool> PENALIZE_TAP_SCORE_NONE ( "Player", "PenalizeTapScoreNone" ); ThemeMetric<bool> PENALIZE_TAP_SCORE_NONE ( "Player", "PenalizeTapScoreNone" );
ThemeMetric<bool> JUDGE_HOLD_NOTES_ON_SAME_ROW_TOGETHER ( "Player", "JudgeHoldNotesOnSameRowTogether" ); ThemeMetric<bool> JUDGE_HOLD_NOTES_ON_SAME_ROW_TOGETHER ( "Player", "JudgeHoldNotesOnSameRowTogether" );
@@ -595,6 +596,15 @@ void Player::Update( float fDeltaTime )
if( m_bPaused ) if( m_bPaused )
return; return;
//
// Check for a strum miss
//
if( m_pPlayerState->m_fLastStrumMusicSeconds != -1 &&
m_pPlayerState->m_fLastStrumMusicSeconds + g_fTimingWindowStrum < GAMESTATE->m_fMusicSeconds )
{
DoStrumMiss();
}
// //
// Check for TapNote misses // Check for TapNote misses
// //
@@ -1213,6 +1223,15 @@ void Player::Fret( int col, int row, const RageTimer &tm, bool bHeld, bool bRele
m_vbFretIsDown[ col ] = !bRelease; m_vbFretIsDown[ col ] = !bRelease;
// Handle changing fret during a strum
if( m_pPlayerState->m_fLastStrumMusicSeconds != -1 )
{
LOG->Trace( "StrumTry" );
StepStrumHopo( col, row, tm, bHeld, bRelease, ButtonType_StrumFretsChanged );
}
// Handle hammer-ons and pull-offs // Handle hammer-ons and pull-offs
const float fPositionSeconds = GAMESTATE->m_fMusicSeconds - tm.Ago(); const float fPositionSeconds = GAMESTATE->m_fMusicSeconds - tm.Ago();
int iHopoCol = -1; int iHopoCol = -1;
@@ -1265,12 +1284,93 @@ void Player::Fret( int col, int row, const RageTimer &tm, bool bHeld, bool bRele
Hopo( iHopoCol, row, tm, bHeld, bRelease ); Hopo( iHopoCol, row, tm, bHeld, bRelease );
} }
void Player::Strum( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease )
{
if( bRelease )
return;
if( m_pPlayerState->m_fLastStrumMusicSeconds != -1 )
{
DoStrumMiss();
}
m_pPlayerState->m_fLastStrumMusicSeconds = GAMESTATE->m_fMusicSeconds;
//StepStrumHopo( col, row, tm, bHeld, bRelease, ButtonType_StrumFretsChanged );
}
void Player::DoTapScoreNone()
{
Message msg( "ScoreNone" );
MESSAGEMAN->Broadcast( msg );
const int iOldCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0;
const int iOldMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0;
/* The only real way to tell if a mine has been scored is if it has disappeared
* but this only works for hit mines so update the scores for avoided mines here. */
if( m_pPrimaryScoreKeeper )
m_pPrimaryScoreKeeper->HandleTapScoreNone();
if( m_pSecondaryScoreKeeper )
m_pSecondaryScoreKeeper->HandleTapScoreNone();
SendComboMessages( iOldCombo, iOldMissCombo );
if( m_pLifeMeter )
m_pLifeMeter->HandleTapScoreNone();
// TODO: Remove use of PlayerNumber
PlayerNumber pn = PLAYER_INVALID;
if( m_pCombinedLifeMeter )
m_pCombinedLifeMeter->HandleTapScoreNone( pn );
if( PENALIZE_TAP_SCORE_NONE )
{
SetJudgment( TNS_Miss, false );
// the ScoreKeeper will subrtract points later.
}
}
void Player::DoStrumMiss()
{
m_pPlayerState->m_fLastStrumMusicSeconds = -1;
DoTapScoreNone();
if( PENALIZE_TAP_SCORE_NONE )
{
const float fSongBeat = GAMESTATE->m_fSongBeat;
const int iSongRow = BeatToNoteRow( fSongBeat );
// Score all active holds to NotHeld
for( int iTrack=0; iTrack<m_NoteData.GetNumTracks(); ++iTrack )
{
// Since this is being called every frame, let's not check the whole array every time.
// Instead, only check 1 beat back. Even 1 is overkill.
const int iStartCheckingAt = max( 0, iSongRow-BeatToNoteRow(1) );
NoteData::iterator begin, end;
m_NoteData.GetTapNoteRangeInclusive( iTrack, iStartCheckingAt, iSongRow+1, begin, end );
for( ; begin != end; ++begin )
{
TapNote &tn = begin->second;
if( tn.HoldResult.bActive )
{
tn.HoldResult.hns = HNS_LetGo;
HandleHoldScore( tn );
SetHoldJudgment( tn.result.tns, tn.HoldResult.hns, iTrack );
}
}
}
}
}
void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease, Player::ButtonType pbt ) void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease, Player::ButtonType pbt )
{ {
if( IsOniDead() ) if( IsOniDead() )
return; return;
// Do everything that depends on a RageTime here and set your breakpoints somewhere after this block // Do everything that depends on a RageTimer here and set your breakpoints somewhere after this block
const float fLastBeatUpdate = GAMESTATE->m_LastBeatUpdate.Ago(); const float fLastBeatUpdate = GAMESTATE->m_LastBeatUpdate.Ago();
const float fPositionSeconds = GAMESTATE->m_fMusicSeconds - tm.Ago(); const float fPositionSeconds = GAMESTATE->m_fMusicSeconds - tm.Ago();
const float fTimeSinceStep = tm.Ago(); const float fTimeSinceStep = tm.Ago();
@@ -1278,15 +1378,13 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
switch( pbt ) switch( pbt )
{ {
DEFAULT_FAIL(pbt); DEFAULT_FAIL(pbt);
case ButtonType_Strum:
if( bRelease )
return;
break;
case ButtonType_Hopo:
bRelease = false;
break;
case ButtonType_Step: case ButtonType_Step:
break; break;
case ButtonType_StrumFretsChanged:
case ButtonType_Hopo:
// releasing should hit regular notes, not lifts
bRelease = false;
break;
} }
const float fSongBeat = GAMESTATE->m_pCurSong ? GAMESTATE->m_pCurSong->GetBeatFromElapsedTime( fPositionSeconds ) : GAMESTATE->m_fSongBeat; const float fSongBeat = GAMESTATE->m_pCurSong ? GAMESTATE->m_pCurSong->GetBeatFromElapsedTime( fPositionSeconds ) : GAMESTATE->m_fSongBeat;
@@ -1357,6 +1455,7 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
// Count calories for this step, unless we're being called because a button is // Count calories for this step, unless we're being called because a button is
// held over a mine or being released. // held over a mine or being released.
// //
// TODO: Move calorie counting into a ScoreKeeper?
if( m_pPlayerStageStats && m_pPlayerState && !bHeld && !bRelease ) if( m_pPlayerStageStats && m_pPlayerState && !bHeld && !bRelease )
{ {
// TODO: remove use of PlayerNumber // TODO: remove use of PlayerNumber
@@ -1414,7 +1513,7 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
switch( pbt ) switch( pbt )
{ {
DEFAULT_FAIL(pbt); DEFAULT_FAIL(pbt);
case ButtonType_Strum: case ButtonType_StrumFretsChanged:
iRowOfOverlappingNoteOrRow = GetClosestNonEmptyRow( BeatToNoteRow(fSongBeat), iStepSearchRows, iStepSearchRows, false ); iRowOfOverlappingNoteOrRow = GetClosestNonEmptyRow( BeatToNoteRow(fSongBeat), iStepSearchRows, iStepSearchRows, false );
break; break;
case ButtonType_Hopo: case ButtonType_Hopo:
@@ -1461,7 +1560,7 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
switch( pbt ) switch( pbt )
{ {
DEFAULT_FAIL(pbt); DEFAULT_FAIL(pbt);
case ButtonType_Strum: case ButtonType_StrumFretsChanged:
pTN = &tnDummy; pTN = &tnDummy;
break; break;
case ButtonType_Hopo: case ButtonType_Hopo:
@@ -1567,7 +1666,7 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
switch( pbt ) switch( pbt )
{ {
DEFAULT_FAIL(pbt); DEFAULT_FAIL(pbt);
case ButtonType_Strum: case ButtonType_StrumFretsChanged:
{ {
bool bNoteRowMatchesFrets = true; bool bNoteRowMatchesFrets = true;
int iFirstNoteCol = -1; int iFirstNoteCol = -1;
@@ -1713,7 +1812,7 @@ done_checking_hopo:
switch( pbt ) switch( pbt )
{ {
DEFAULT_FAIL(pbt); DEFAULT_FAIL(pbt);
case ButtonType_Strum: case ButtonType_StrumFretsChanged:
for( int t=0; t<m_NoteData.GetNumTracks(); t++ ) for( int t=0; t<m_NoteData.GetNumTracks(); t++ )
{ {
TapNote tn = m_NoteData.GetTapNote( t, iRowOfOverlappingNoteOrRow ); TapNote tn = m_NoteData.GetTapNote( t, iRowOfOverlappingNoteOrRow );
@@ -1761,69 +1860,35 @@ done_checking_hopo:
m_pPlayerState->ClearHopoState(); m_pPlayerState->ClearHopoState();
} }
// check for strum end
if( score != TNS_None )
{
switch( pbt )
{
DEFAULT_FAIL(pbt);
case ButtonType_Step:
break;
case ButtonType_StrumFretsChanged:
m_pPlayerState->m_fLastStrumMusicSeconds = -1;
break;
case ButtonType_Hopo:
break;
}
}
if( score == TNS_None ) if( score == TNS_None )
{ {
switch( pbt ) switch( pbt )
{ {
DEFAULT_FAIL(pbt); DEFAULT_FAIL(pbt);
case ButtonType_Strum:
case ButtonType_Step: case ButtonType_Step:
{ DoTapScoreNone();
Message msg( "ScoreNone" );
MESSAGEMAN->Broadcast( msg );
const int iOldCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurCombo : 0;
const int iOldMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->m_iCurMissCombo : 0;
/* The only real way to tell if a mine has been scored is if it has disappeared
* but this only works for hit mines so update the scores for avoided mines here. */
if( m_pPrimaryScoreKeeper )
m_pPrimaryScoreKeeper->HandleTapScoreNone();
if( m_pSecondaryScoreKeeper )
m_pSecondaryScoreKeeper->HandleTapScoreNone();
SendComboMessages( iOldCombo, iOldMissCombo );
if( m_pLifeMeter )
m_pLifeMeter->HandleTapScoreNone();
// TODO: Remove use of PlayerNumber
PlayerNumber pn = PLAYER_INVALID;
if( m_pCombinedLifeMeter )
m_pCombinedLifeMeter->HandleTapScoreNone( pn );
if( PENALIZE_TAP_SCORE_NONE )
{
SetJudgment( TNS_Miss, false );
// the ScoreKeeper will subrtract points later.
// Score all active holds to NotHeld
for( int iTrack=0; iTrack<m_NoteData.GetNumTracks(); ++iTrack )
{
// Since this is being called every frame, let's not check the whole array every time.
// Instead, only check 1 beat back. Even 1 is overkill.
const int iStartCheckingAt = max( 0, iSongRow-BeatToNoteRow(1) );
NoteData::iterator begin, end;
m_NoteData.GetTapNoteRangeInclusive( iTrack, iStartCheckingAt, iSongRow+1, begin, end );
for( ; begin != end; ++begin )
{
TapNote &tn = begin->second;
if( tn.HoldResult.bActive )
{
tn.HoldResult.hns = HNS_LetGo;
HandleHoldScore( tn );
SetHoldJudgment( tn.result.tns, tn.HoldResult.hns, iTrack );
}
}
}
}
}
break; break;
case ButtonType_StrumFretsChanged:
case ButtonType_Hopo: case ButtonType_Hopo:
break; break;
} }
} }
if( score != TNS_None ) if( score != TNS_None )
@@ -1844,7 +1909,7 @@ done_checking_hopo:
switch( pbt ) switch( pbt )
{ {
DEFAULT_FAIL(pbt); DEFAULT_FAIL(pbt);
case ButtonType_Strum: case ButtonType_StrumFretsChanged:
for( int i=0; i<m_NoteData.GetNumTracks(); i++ ) for( int i=0; i<m_NoteData.GetNumTracks(); i++ )
{ {
const TapNote &tn = m_NoteData.GetTapNote( i, iRowOfOverlappingNoteOrRow ); const TapNote &tn = m_NoteData.GetTapNote( i, iRowOfOverlappingNoteOrRow );
@@ -1869,7 +1934,7 @@ done_checking_hopo:
switch( pbt ) switch( pbt )
{ {
DEFAULT_FAIL(pbt); DEFAULT_FAIL(pbt);
case ButtonType_Strum: case ButtonType_StrumFretsChanged:
{ {
// only pulse the shortest string fret // only pulse the shortest string fret
int iLastFret = -1; int iLastFret = -1;
+10 -2
View File
@@ -122,10 +122,18 @@ public:
// Called when a fret, step, or strum type button changes // Called when a fret, step, or strum type button changes
void Fret( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease ); void Fret( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease );
enum ButtonType { ButtonType_Step, ButtonType_Strum, ButtonType_Hopo };
// Called when the strum bar is pressed down
void Strum( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease );
// Called when the strum window passes without a row being hit
void DoStrumMiss();
void DoTapScoreNone();
enum ButtonType { ButtonType_Step, ButtonType_StrumFretsChanged, ButtonType_Hopo };
void StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease, ButtonType gbt ); void StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease, ButtonType gbt );
void Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease ) { StepStrumHopo(col, row, tm, bHeld, bRelease, ButtonType_Step); } void Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease ) { StepStrumHopo(col, row, tm, bHeld, bRelease, ButtonType_Step); }
void Strum( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease ) { StepStrumHopo(col, row, tm, bHeld, bRelease, ButtonType_Strum); }
// called by Fret for Hammer-ons and Pull-offs // called by Fret for Hammer-ons and Pull-offs
void Hopo( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease ) { StepStrumHopo(col, row, tm, bHeld, bRelease, ButtonType_Hopo); } void Hopo( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease ) { StepStrumHopo(col, row, tm, bHeld, bRelease, ButtonType_Hopo); }
+3
View File
@@ -38,6 +38,9 @@ void PlayerState::Reset()
for( int i=0; i<NUM_INVENTORY_SLOTS; i++ ) for( int i=0; i<NUM_INVENTORY_SLOTS; i++ )
m_Inventory[i].MakeBlank(); m_Inventory[i].MakeBlank();
m_fLastStrumMusicSeconds = -1;
ClearHopoState();
} }
void PlayerState::Update( float fDelta ) void PlayerState::Update( float fDelta )
+1
View File
@@ -35,6 +35,7 @@ public:
enum HealthState { HOT, ALIVE, DANGER, DEAD }; enum HealthState { HOT, ALIVE, DANGER, DEAD };
HealthState m_HealthState; HealthState m_HealthState;
float m_fLastStrumMusicSeconds; // Set to the MusicSeconds of when the a strum button was pressed. If -1, the strum window has passed.
float m_fLastHopoNoteMusicSeconds; // Set to the MusicSeconds of the last note successfully strummed or hammered in a hopochain, -1, then there is no current hopo chain float m_fLastHopoNoteMusicSeconds; // Set to the MusicSeconds of the last note successfully strummed or hammered in a hopochain, -1, then there is no current hopo chain
int m_iLastHopoNoteCol; // if -1, then there is no current hopo chain int m_iLastHopoNoteCol; // if -1, then there is no current hopo chain