diff --git a/stepmania/src/LifeMeterBar.cpp b/stepmania/src/LifeMeterBar.cpp index a173e3b3c1..a2d51e9c94 100644 --- a/stepmania/src/LifeMeterBar.cpp +++ b/stepmania/src/LifeMeterBar.cpp @@ -359,8 +359,8 @@ void LifeMeterBar::ChangeLife( TapNoteScore score ) m_iComboToRegainLife = max( m_iComboToRegainLife, NewComboToRegainLife ); } - /* If we've already failed, there's no point in letting them fill up the bar again. Except for passmark mode */ - if( GAMESTATE->m_CurStageStats.bFailed[m_PlayerNumber] && GAMESTATE->m_SongOptions.m_FailType != SongOptions::FAIL_PASSMARK) + /* If we've already failed, there's no point in letting them fill up the bar again. */ + if( GAMESTATE->m_CurStageStats.bFailed[m_PlayerNumber] ) fDeltaLife = 0; switch( GAMESTATE->m_SongOptions.m_DrainType ) diff --git a/stepmania/src/PlayerOptions.cpp b/stepmania/src/PlayerOptions.cpp index a7b91a74ef..a59f7b8821 100644 --- a/stepmania/src/PlayerOptions.cpp +++ b/stepmania/src/PlayerOptions.cpp @@ -34,6 +34,7 @@ void PlayerOptions::Init() m_fBlind = 0; m_SpeedfBlind = 1.0f; m_fPerspectiveTilt = 0; m_SpeedfPerspectiveTilt = 1.0f; m_fSkew = 0; m_SpeedfSkew = 1.0f; + m_fPassmark = 0; m_SpeedfPassmark = 1.0f; m_Turn = TURN_NONE; m_Transform = TRANSFORM_NONE; m_bHoldNotes = true; @@ -62,6 +63,7 @@ void PlayerOptions::Approach( const PlayerOptions& other, float fDeltaSeconds ) APP( fBlind ); APP( fPerspectiveTilt ); APP( fSkew ); + APP( fPassmark ); } static CString AddPart( float level, CString name ) @@ -69,7 +71,7 @@ static CString AddPart( float level, CString name ) if( level == 0 ) return ""; - const CString LevelStr = (level == 1)? "": ssprintf( "%i%% ", int(level*100) ); + const CString LevelStr = (level == 1)? "": ssprintf( "%i%% ", (int) roundf(level*100) ); return LevelStr + name + ", "; } @@ -130,6 +132,8 @@ CString PlayerOptions::GetString() const sReturn += AddPart( m_fBlind, "Blind"); + sReturn += AddPart( m_fPassmark, "Passmark"); + switch( m_Turn ) { case TURN_NONE: break; @@ -187,6 +191,7 @@ void PlayerOptions::FromString( CString sOptions ) CStringArray asBits; split( sOptions, ",", asBits, true ); + LOG->Trace("parse %s", sOptions.c_str()); for( unsigned i=0; iTrace("'%s' %f %f", asParts[j].c_str(), level, speed); } sBit = asParts[asParts.size()-1]; @@ -282,6 +288,7 @@ void PlayerOptions::FromString( CString sOptions ) else if( sBit == "nofreeze" ) m_bHoldNotes = !on; else if( sBit == "dark" ) SET_FLOAT( fDark ) else if( sBit == "blind" ) SET_FLOAT( fBlind ) + else if( sBit == "passmark" ) SET_FLOAT( fPassmark ) else if( sBit == "timingassist")m_bTimingAssist = on; else if( sBit == "protiming") m_bProTiming = on; else if( sBit == "overhead" ) { m_fSkew = 0; m_fPerspectiveTilt = 0; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; } diff --git a/stepmania/src/PlayerOptions.h b/stepmania/src/PlayerOptions.h index f7e7e887c3..5b633ba844 100644 --- a/stepmania/src/PlayerOptions.h +++ b/stepmania/src/PlayerOptions.h @@ -91,6 +91,10 @@ struct PlayerOptions float m_fPerspectiveTilt, m_SpeedfPerspectiveTilt; // -1 = near, 0 = overhead, +1 = space float m_fSkew, m_SpeedfSkew; // 0 = vanish point is in center of player, 1 = vanish point is in center of screen + /* If this is > 0, then the player must have life above this value at the end of + * the song to pass. This is independent of SongOptions::m_FailType. */ + float m_fPassmark, m_SpeedfPassmark; + Turn m_Turn; Transform m_Transform; bool m_bHoldNotes; diff --git a/stepmania/src/ScreenGameplay.cpp b/stepmania/src/ScreenGameplay.cpp index 81eb9a04ae..ebd9c1e9ab 100644 --- a/stepmania/src/ScreenGameplay.cpp +++ b/stepmania/src/ScreenGameplay.cpp @@ -911,7 +911,7 @@ float ScreenGameplay::StartPlayingSong(float MinTimeToNotes, float MinTimeToMusi return fFirstSecond - fStartSecond; } -bool ScreenGameplay::OneIsHot() +bool ScreenGameplay::OneIsHot() const { for( int p=0; pIsPlayerEnabled(PlayerNumber(p)) ) @@ -921,7 +921,7 @@ bool ScreenGameplay::OneIsHot() return false; } -bool ScreenGameplay::AllAreInDanger() +bool ScreenGameplay::AllAreInDanger() const { for( int p=0; pIsPlayerEnabled(PlayerNumber(p)) ) @@ -931,37 +931,18 @@ bool ScreenGameplay::AllAreInDanger() return true; } -bool ScreenGameplay::AllAreFailing() +bool ScreenGameplay::AllAreFailing() const { - - if( GAMESTATE->m_SongOptions.m_FailType == SongOptions::FAIL_PASSMARK ) - { - bool bFoundAPasser = true; // assume nobody passed until proven otherwise - - for( int p=0; pIsPlayerEnabled(PlayerNumber(p)) ) - if( (m_pLifeMeter[p]->GetLife()) > 0.7f ) // 70 % is the pass mark - { - bFoundAPasser = false; - } - } - return bFoundAPasser; - } - else - { - for( int p=0; pIsPlayerEnabled(PlayerNumber(p)) ) if( (m_pLifeMeter[p] && !m_pLifeMeter[p]->IsFailing()) || (m_pCombinedLifeMeter && !m_pCombinedLifeMeter->IsFailing((PlayerNumber)p)) ) return false; - } return true; } -bool ScreenGameplay::AllFailedEarlier() +bool ScreenGameplay::AllFailedEarlier() const { for( int p=0; pIsPlayerEnabled(p) && !GAMESTATE->m_CurStageStats.bFailedEarlier[p] ) @@ -1101,7 +1082,7 @@ void ScreenGameplay::Update( float fDeltaTime ) // show it if everyone is already failing: it's already too late and it's // annoying for it to show for the entire duration of a song. // - if( GAMESTATE->m_SongOptions.m_FailType != SongOptions::FAIL_OFF || GAMESTATE->m_SongOptions.m_FailType != SongOptions::FAIL_PASSMARK ) + if( GAMESTATE->m_SongOptions.m_FailType != SongOptions::FAIL_OFF ) { if( AllAreInDanger() && !AllAreFailing() ) m_Background.TurnDangerOn(); @@ -1624,23 +1605,40 @@ void ScreenGameplay::HandleScreenMessage( const ScreenMessage SM ) if( !GAMESTATE->IsPlayerEnabled(p) ) continue; + /* If either player's passmark is enabled, check it. */ + if( GAMESTATE->m_PlayerOptions[p].m_fPassmark > 0 && + m_pLifeMeter[p] && + m_pLifeMeter[p]->GetLife() < GAMESTATE->m_PlayerOptions[p].m_fPassmark ) + { + LOG->Trace("Player %i failed: life %f is under %f", + p+1, m_pLifeMeter[p]->GetLife(), GAMESTATE->m_PlayerOptions[p].m_fPassmark ); + GAMESTATE->m_CurStageStats.bFailed[p] = true; + } + if( !GAMESTATE->m_CurStageStats.bFailed[p] ) GAMESTATE->m_CurStageStats.iSongsPassed[p]++; } - - if( (GAMESTATE->m_SongOptions.m_FailType == SongOptions::FAIL_END_OF_SONG || GAMESTATE->m_SongOptions.m_FailType == SongOptions::FAIL_PASSMARK ) && AllAreFailing() ) + /* Mark failure. This was possibly already done by UpdateCheckFail, but + * not always, if m_bTwoPlayerRecovery is set. */ + if( GAMESTATE->m_SongOptions.m_FailType != SongOptions::FAIL_OFF ) { - if( m_DancingState == STATE_OUTRO ) // ScreenGameplay already ended - return; // ignore - m_DancingState = STATE_OUTRO; - - GAMESTATE->RemoveAllActiveAttacks(); - - this->PostScreenMessage( SM_BeginFailed, 0 ); + if( (m_pLifeMeter[p] && !m_pLifeMeter[p]->IsFailing()) || + (m_pCombinedLifeMeter && !m_pCombinedLifeMeter->IsFailing((PlayerNumber)p)) ) + GAMESTATE->m_CurStageStats.bFailed[p] = true; } - else if( !IsLastSong() ) + + /* If all players have *really* failed (bFailed, not the life meter or + * bFailedEarlier): */ + bool bAllReallyFailed = true; + for( p=0; pIsPlayerEnabled(PlayerNumber(p)) ) + if( !GAMESTATE->m_CurStageStats.bFailed[p] ) + bAllReallyFailed = false; + + if( !bAllReallyFailed && !IsLastSong() ) { + /* Next song. */ for( p=0; pIsPlayerEnabled(p) && !GAMESTATE->m_CurStageStats.bFailed[p] ) { @@ -1663,50 +1661,56 @@ void ScreenGameplay::HandleScreenMessage( const ScreenMessage SM ) GAMESTATE->m_pCurSong = pCurSong; m_NextSongOut.StartTransitioning( SM_LoadNextSong ); + return; } - else // IsLastSong + + /* End round. */ + if( m_DancingState == STATE_OUTRO ) // ScreenGameplay already ended + return; // ignore + m_DancingState = STATE_OUTRO; + + GAMESTATE->RemoveAllActiveAttacks(); + + if( bAllReallyFailed ) { - if( m_DancingState == STATE_OUTRO ) // ScreenGameplay already ended - return; // ignore - m_DancingState = STATE_OUTRO; + this->PostScreenMessage( SM_BeginFailed, 0 ); + return; + } - GAMESTATE->RemoveAllActiveAttacks(); - - // do they deserve an extra stage? - if( GAMESTATE->HasEarnedExtraStage() ) + // do they deserve an extra stage? + if( GAMESTATE->HasEarnedExtraStage() ) + { + TweenOffScreen(); + m_Extra.StartTransitioning( SM_GoToStateAfterCleared ); + SOUND->PlayOnceFromDir( ANNOUNCER->GetPathTo("gameplay extra") ); + } + else + { + TweenOffScreen(); + + switch( GAMESTATE->m_PlayMode ) { - TweenOffScreen(); - m_Extra.StartTransitioning( SM_GoToStateAfterCleared ); - SOUND->PlayOnceFromDir( ANNOUNCER->GetPathTo("gameplay extra") ); - } - else - { - TweenOffScreen(); - - switch( GAMESTATE->m_PlayMode ) + case PLAY_MODE_BATTLE: + case PLAY_MODE_RAVE: { - case PLAY_MODE_BATTLE: - case PLAY_MODE_RAVE: + PlayerNumber winner = GAMESTATE->GetBestPlayer(); + switch( winner ) { - PlayerNumber winner = GAMESTATE->GetBestPlayer(); - switch( winner ) - { - case PLAYER_INVALID: - m_Draw.StartTransitioning( SM_GoToStateAfterCleared ); - break; - default: - m_Win[winner].StartTransitioning( SM_GoToStateAfterCleared ); - break; - } + case PLAYER_INVALID: + m_Draw.StartTransitioning( SM_GoToStateAfterCleared ); + break; + default: + m_Win[winner].StartTransitioning( SM_GoToStateAfterCleared ); + break; } - break; - default: - m_Cleared.StartTransitioning( SM_GoToStateAfterCleared ); - break; } - - SOUND->PlayOnceFromDir( ANNOUNCER->GetPathTo("gameplay cleared") ); + break; + default: + m_Cleared.StartTransitioning( SM_GoToStateAfterCleared ); + break; } + + SOUND->PlayOnceFromDir( ANNOUNCER->GetPathTo("gameplay cleared") ); } } break; diff --git a/stepmania/src/ScreenGameplay.h b/stepmania/src/ScreenGameplay.h index 2002aeaa36..2a61afdc89 100644 --- a/stepmania/src/ScreenGameplay.h +++ b/stepmania/src/ScreenGameplay.h @@ -76,10 +76,10 @@ protected: float StartPlayingSong(float MinTimeToNotes, float MinTimeToMusic); void ShowSavePrompt( ScreenMessage SM_SendWhenDone ); - bool OneIsHot(); - bool AllAreInDanger(); - bool AllAreFailing(); - bool AllFailedEarlier(); + bool OneIsHot() const; + bool AllAreInDanger() const; + bool AllAreFailing() const; + bool AllFailedEarlier() const; bool IsTimeToPlayTicks() const; void UpdateLyrics( float fDeltaTime ); void UpdateCheckFail(); diff --git a/stepmania/src/ScreenOptionsMasterPrefs.cpp b/stepmania/src/ScreenOptionsMasterPrefs.cpp index af8af3012c..33f09c946b 100644 --- a/stepmania/src/ScreenOptionsMasterPrefs.cpp +++ b/stepmania/src/ScreenOptionsMasterPrefs.cpp @@ -320,8 +320,7 @@ static void DefaultFailType( int &sel, bool ToSel, const CStringArray &choices ) { case 0: so.m_FailType = SongOptions::FAIL_ARCADE; break; case 1: so.m_FailType = SongOptions::FAIL_END_OF_SONG; break; - case 2: so.m_FailType = SongOptions::FAIL_PASSMARK; break; - case 3: so.m_FailType = SongOptions::FAIL_OFF; break; + case 2: so.m_FailType = SongOptions::FAIL_OFF; break; default: ASSERT(0); } @@ -451,7 +450,7 @@ static const ConfOption g_ConfOptions[] = ConfOption( "Progressive\nLifebar", ProgressiveLifebar, "OFF","1","2","3","4","5","6","7","8"), ConfOption( "Progressive\nStage Lifebar",ProgressiveStageLifebar, "OFF","1","2","3","4","5","6","7","8","INSANITY"), ConfOption( "Progressive\nNonstop Lifebar",ProgressiveNonstopLifebar,"OFF","1","2","3","4","5","6","7","8","INSANITY"), - ConfOption( "Default\nFail Type", DefaultFailType, "ARCADE","END OF SONG","PASSMARK","OFF" ), + ConfOption( "Default\nFail Type", DefaultFailType, "ARCADE","END OF SONG","OFF" ), ConfOption( "Coins Per\nCredit", CoinsPerCredit, "1","2","3","4","5","6","7","8" ), ConfOption( "Joint\nPremium", JointPremium, "OFF","ON" ), ConfOption( "Show Song\nOptions", ShowSongOptions, "HIDE","SHOW","ASK" ), diff --git a/stepmania/src/SongOptions.cpp b/stepmania/src/SongOptions.cpp index 67d722afdc..ce7857018c 100644 --- a/stepmania/src/SongOptions.cpp +++ b/stepmania/src/SongOptions.cpp @@ -49,7 +49,6 @@ CString SongOptions::GetString() const { case FAIL_ARCADE: break; case FAIL_END_OF_SONG: sReturn += "FailEndOfSong, "; break; - case FAIL_PASSMARK: sReturn += "FailPassMark"; break; case FAIL_OFF: sReturn += "FailOff, "; break; } @@ -117,7 +116,6 @@ void SongOptions::FromString( CString sOptions ) else if( sBit == "death" ) m_DrainType = DRAIN_SUDDEN_DEATH; else if( sBit == "normal-drain" ) m_DrainType = DRAIN_NORMAL; else if( sBit == "failarcade" ) m_FailType = FAIL_ARCADE; - else if( sBit == "failpassmark" ) m_FailType = FAIL_PASSMARK; else if( sBit == "failendofsong" ) m_FailType = FAIL_END_OF_SONG; else if( sBit == "failoff" ) m_FailType = FAIL_OFF; else if( sBit == "assisttick" ) m_bAssistTick = on; diff --git a/stepmania/src/SongOptions.h b/stepmania/src/SongOptions.h index f3ae8dae88..df111d1f6b 100644 --- a/stepmania/src/SongOptions.h +++ b/stepmania/src/SongOptions.h @@ -20,7 +20,7 @@ struct SongOptions enum DrainType { DRAIN_NORMAL, DRAIN_NO_RECOVER, DRAIN_SUDDEN_DEATH }; DrainType m_DrainType; // only used with LifeBar int m_iBatteryLives; - enum FailType { FAIL_ARCADE=0, FAIL_END_OF_SONG, FAIL_PASSMARK, FAIL_OFF }; + enum FailType { FAIL_ARCADE=0, FAIL_END_OF_SONG, FAIL_OFF }; FailType m_FailType; float m_fMusicRate; bool m_bAssistTick, m_bAutoSync, m_bSaveScore;