diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs
index e6d9dd4cd0..d9043df1d5 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs
@@ -26,9 +26,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
protected override bool PauseOnFocusLost => false;
- // Disallow fails in multiplayer for now.
- protected override bool CheckModsAllowFailure() => false;
-
protected override UserActivity InitialActivity => new UserActivity.InMultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value);
[Resolved]
@@ -55,6 +52,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
AllowPause = false,
AllowRestart = false,
+ AllowFailAnimation = false,
AllowSkipping = room.AutoSkip.Value,
AutomaticallySkipIntro = room.AutoSkip.Value,
AlwaysShowLeaderboard = true,
diff --git a/osu.Game/Screens/Play/GameplayState.cs b/osu.Game/Screens/Play/GameplayState.cs
index c2162d4df2..cc399a0fbe 100644
--- a/osu.Game/Screens/Play/GameplayState.cs
+++ b/osu.Game/Screens/Play/GameplayState.cs
@@ -46,7 +46,7 @@ namespace osu.Game.Screens.Play
public bool HasPassed { get; set; }
///
- /// Whether the user failed during gameplay.
+ /// Whether the user failed during gameplay. This is only set when the gameplay session has completed due to the fail.
///
public bool HasFailed { get; set; }
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index c960ac357f..df50e35986 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -735,7 +735,7 @@ namespace osu.Game.Screens.Play
}
// Only show the completion screen if the player hasn't failed
- if (HealthProcessor.HasFailed)
+ if (GameplayState.HasFailed)
return;
GameplayState.HasPassed = true;
@@ -924,37 +924,44 @@ namespace osu.Game.Screens.Play
if (!CheckModsAllowFailure())
return false;
- Debug.Assert(!GameplayState.HasFailed);
- Debug.Assert(!GameplayState.HasPassed);
- Debug.Assert(!GameplayState.HasQuit);
+ if (Configuration.AllowFailAnimation)
+ {
+ Debug.Assert(!GameplayState.HasFailed);
+ Debug.Assert(!GameplayState.HasPassed);
+ Debug.Assert(!GameplayState.HasQuit);
- GameplayState.HasFailed = true;
+ GameplayState.HasFailed = true;
- updateGameplayState();
+ updateGameplayState();
- // There is a chance that we could be in a paused state as the ruleset's internal clock (see FrameStabilityContainer)
- // could process an extra frame after the GameplayClock is stopped.
- // In such cases we want the fail state to precede a user triggered pause.
- if (PauseOverlay.State.Value == Visibility.Visible)
- PauseOverlay.Hide();
+ // There is a chance that we could be in a paused state as the ruleset's internal clock (see FrameStabilityContainer)
+ // could process an extra frame after the GameplayClock is stopped.
+ // In such cases we want the fail state to precede a user triggered pause.
+ if (PauseOverlay.State.Value == Visibility.Visible)
+ PauseOverlay.Hide();
- failAnimationContainer.Start();
+ failAnimationContainer.Start();
- // Failures can be triggered either by a judgement, or by a mod.
- //
- // For the case of a judgement, due to ordering considerations, ScoreProcessor will not have received
- // the final judgement which triggered the failure yet (see DrawableRuleset.NewResult handling above).
- //
- // A schedule here ensures that any lingering judgements from the current frame are applied before we
- // finalise the score as "failed".
- Schedule(() =>
+ // Failures can be triggered either by a judgement, or by a mod.
+ //
+ // For the case of a judgement, due to ordering considerations, ScoreProcessor will not have received
+ // the final judgement which triggered the failure yet (see DrawableRuleset.NewResult handling above).
+ //
+ // A schedule here ensures that any lingering judgements from the current frame are applied before we
+ // finalise the score as "failed".
+ Schedule(() =>
+ {
+ ScoreProcessor.FailScore(Score.ScoreInfo);
+ OnFail();
+
+ if (GameplayState.Mods.OfType().Any(m => m.RestartOnFail))
+ Restart(true);
+ });
+ }
+ else
{
ScoreProcessor.FailScore(Score.ScoreInfo);
- OnFail();
-
- if (GameplayState.Mods.OfType().Any(m => m.RestartOnFail))
- Restart(true);
- });
+ }
return true;
}
diff --git a/osu.Game/Screens/Play/PlayerConfiguration.cs b/osu.Game/Screens/Play/PlayerConfiguration.cs
index 122e25f406..466a691118 100644
--- a/osu.Game/Screens/Play/PlayerConfiguration.cs
+++ b/osu.Game/Screens/Play/PlayerConfiguration.cs
@@ -15,6 +15,12 @@ namespace osu.Game.Screens.Play
///
public bool ShowResults { get; set; } = true;
+ ///
+ /// Whether the fail animation / screen should be triggered on failing.
+ /// If false, the score will still be marked as failed but gameplay will continue.
+ ///
+ public bool AllowFailAnimation { get; set; } = true;
+
///
/// Whether the player should be allowed to trigger a restart.
///