diff --git a/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs
new file mode 100644
index 0000000000..dbcd1b93af
--- /dev/null
+++ b/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs
@@ -0,0 +1,44 @@
+using System;
+using OpenTK.Graphics;
+using osu.Framework.Logging;
+using osu.Framework.Graphics;
+using osu.Game.Overlays.Pause;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.GameModes.Testing;
+using osu.Framework.Graphics.UserInterface;
+
+namespace osu.Desktop.VisualTests.Tests
+{
+ class TestCasePauseOverlay : TestCase
+ {
+ public override string Name => @"PauseOverlay";
+
+ public override string Description => @"Tests the pause overlay";
+
+ private PauseOverlay pauseOverlay;
+ private int retryCount;
+
+ public override void Reset()
+ {
+ base.Reset();
+
+ Add(pauseOverlay = new PauseOverlay
+ {
+ Depth = -1,
+ OnResume = () => Logger.Log(@"Resume"),
+ OnRetry = () => Logger.Log(@"Retry"),
+ OnQuit = () => Logger.Log(@"Quit")
+ });
+ AddButton("Pause", pauseOverlay.Show);
+ AddButton("Add Retry", delegate
+ {
+ retryCount++;
+ pauseOverlay.Retries = retryCount;
+ });
+
+ retryCount = 0;
+ }
+ }
+}
diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj
index 70dd0fee46..cd24771967 100644
--- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj
+++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj
@@ -186,6 +186,7 @@
+
diff --git a/osu.Game/Overlays/Pause/PauseButton.cs b/osu.Game/Overlays/Pause/PauseButton.cs
new file mode 100644
index 0000000000..ed56cc7b85
--- /dev/null
+++ b/osu.Game/Overlays/Pause/PauseButton.cs
@@ -0,0 +1,233 @@
+using OpenTK;
+using OpenTK.Graphics;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Transformations;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Audio.Sample;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Backgrounds;
+
+namespace osu.Game.Overlays.Pause
+{
+ public class PauseButton : ClickableContainer
+ {
+ private const float hoverWidth = 0.9f;
+ private const float hoverDuration = 500;
+ private const float glowFadeDuration = 250;
+ private const float clickDuration = 200;
+
+ private Color4 backgroundColour = OsuColour.Gray(34);
+
+ private Color4 buttonColour;
+ public Color4 ButtonColour
+ {
+ get
+ {
+ return buttonColour;
+ }
+ set
+ {
+ buttonColour = value;
+ updateGlow();
+ if (colourContainer == null) return;
+ colourContainer.Colour = ButtonColour;
+ }
+ }
+
+ private string text;
+ public string Text
+ {
+ get
+ {
+ return text;
+ }
+ set
+ {
+ text = value;
+ if (spriteText == null) return;
+ spriteText.Text = Text;
+ }
+ }
+
+ public AudioSample SampleClick, SampleHover;
+
+ private Container backgroundContainer, colourContainer, glowContainer;
+ private Box leftGlow, centerGlow, rightGlow;
+ private SpriteText spriteText;
+
+ private bool didClick; // Used for making sure that the OnMouseDown animation can call instead of OnHoverLost's when clicking
+
+ public override bool Contains(Vector2 screenSpacePos) => backgroundContainer.Contains(screenSpacePos);
+
+ protected override bool OnMouseDown(Framework.Input.InputState state, MouseDownEventArgs args)
+ {
+ didClick = true;
+ colourContainer.ResizeTo(new Vector2(1.5f, 1f), clickDuration, EasingTypes.In);
+ flash();
+ SampleClick?.Play();
+ Action?.Invoke();
+
+ Delay(clickDuration);
+ Schedule(delegate {
+ colourContainer.ResizeTo(new Vector2(0.8f, 1f), 0, EasingTypes.None);
+ spriteText.Spacing = Vector2.Zero;
+ glowContainer.FadeOut();
+ });
+
+ return true;
+ }
+
+ protected override bool OnClick(Framework.Input.InputState state) => false;
+
+ protected override bool OnHover(Framework.Input.InputState state)
+ {
+ colourContainer.ResizeTo(new Vector2(hoverWidth, 1f), hoverDuration, EasingTypes.OutElastic);
+ spriteText.TransformSpacingTo(new Vector2(3f, 0f), hoverDuration, EasingTypes.OutElastic);
+ glowContainer.FadeIn(glowFadeDuration, EasingTypes.Out);
+ SampleHover?.Play();
+ return true;
+ }
+
+ protected override void OnHoverLost(Framework.Input.InputState state)
+ {
+ if (!didClick)
+ {
+ colourContainer.ResizeTo(new Vector2(0.8f, 1f), hoverDuration, EasingTypes.OutElastic);
+ spriteText.TransformSpacingTo(Vector2.Zero, hoverDuration, EasingTypes.OutElastic);
+ glowContainer.FadeOut(glowFadeDuration, EasingTypes.Out);
+ }
+
+ didClick = false;
+ }
+
+ private void flash()
+ {
+ var flash = new Box
+ {
+ RelativeSizeAxes = Axes.Both
+ };
+
+ colourContainer.Add(flash);
+
+ flash.Colour = ButtonColour;
+ flash.BlendingMode = BlendingMode.Additive;
+ flash.Alpha = 0.3f;
+ flash.FadeOutFromOne(clickDuration);
+ flash.Expire();
+ }
+
+ private void updateGlow()
+ {
+ leftGlow.ColourInfo = ColourInfo.GradientHorizontal(new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f), ButtonColour);
+ centerGlow.Colour = ButtonColour;
+ rightGlow.ColourInfo = ColourInfo.GradientHorizontal(ButtonColour, new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f));
+ }
+
+ public PauseButton()
+ {
+ Children = new Drawable[]
+ {
+ backgroundContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Width = 1f,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = backgroundColour
+ }
+ }
+ },
+ glowContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Width = 1f,
+ Alpha = 0f,
+ Children = new Drawable[]
+ {
+ leftGlow = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Origin = Anchor.TopLeft,
+ Anchor = Anchor.TopLeft,
+ Width = 0.125f
+ },
+ centerGlow = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Width = 0.75f
+ },
+ rightGlow = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Origin = Anchor.TopRight,
+ Anchor = Anchor.TopRight,
+ Width = 0.125f
+ }
+ }
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Masking = true,
+ Children = new Drawable[]
+ {
+ colourContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Width = 0.8f,
+ Masking = true,
+ EdgeEffect = new EdgeEffect
+ {
+ Type = EdgeEffectType.Shadow,
+ Colour = Color4.Black.Opacity(0.2f),
+ Radius = 5
+ },
+ Colour = ButtonColour,
+ Shear = new Vector2(0.2f, 0),
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ EdgeSmoothness = new Vector2(2, 0),
+ RelativeSizeAxes = Axes.Both
+ },
+ new Triangles
+ {
+ BlendingMode = BlendingMode.Additive,
+ RelativeSizeAxes = Axes.Both,
+ TriangleScale = 4,
+ Alpha = 0.05f,
+ Shear = new Vector2(-0.2f, 0)
+ }
+ }
+ }
+ }
+ },
+ spriteText = new SpriteText
+ {
+ Text = Text,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ TextSize = 28,
+ Font = "Exo2.0-Bold",
+ Shadow = true,
+ ShadowColour = new Color4(0, 0, 0, 0.1f),
+ Colour = Color4.White
+ }
+ };
+
+ updateGlow();
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Pause/PauseOverlay.cs b/osu.Game/Overlays/Pause/PauseOverlay.cs
new file mode 100644
index 0000000000..733133f005
--- /dev/null
+++ b/osu.Game/Overlays/Pause/PauseOverlay.cs
@@ -0,0 +1,212 @@
+using System;
+using OpenTK;
+using OpenTK.Input;
+using OpenTK.Graphics;
+using osu.Game.Graphics;
+using osu.Framework.Input;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Transformations;
+using System.Threading.Tasks;
+
+namespace osu.Game.Overlays.Pause
+{
+ public class PauseOverlay : OverlayContainer
+ {
+ private const int transition_duration = 200;
+ private const int button_height = 70;
+ private const float background_alpha = 0.75f;
+
+ public Action OnResume;
+ public Action OnRetry;
+ public Action OnQuit;
+
+ public int Retries
+ {
+ set
+ {
+ if (retryCounterContainer != null)
+ {
+ // "You've retried 1,065 times in this session"
+ // "You've retried 1 time in this session"
+
+ retryCounterContainer.Children = new Drawable[]
+ {
+ new SpriteText
+ {
+ Text = "You've retried ",
+ Shadow = true,
+ ShadowColour = new Color4(0, 0, 0, 0.25f),
+ TextSize = 18
+ },
+ new SpriteText
+ {
+ Text = String.Format("{0:n0}", value),
+ Font = @"Exo2.0-Bold",
+ Shadow = true,
+ ShadowColour = new Color4(0, 0, 0, 0.25f),
+ TextSize = 18
+ },
+ new SpriteText
+ {
+ Text = $" time{((value == 1) ? "" : "s")} in this session",
+ Shadow = true,
+ ShadowColour = new Color4(0, 0, 0, 0.25f),
+ TextSize = 18
+ }
+ };
+ }
+ }
+ }
+
+ private FlowContainer retryCounterContainer;
+
+ public override bool Contains(Vector2 screenSpacePos) => true;
+ public override bool HandleInput => State == Visibility.Visible;
+
+ protected override void PopIn() => FadeIn(transition_duration, EasingTypes.In);
+ protected override void PopOut() => FadeOut(transition_duration, EasingTypes.In);
+
+ protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ {
+ if (args.Key == Key.Escape)
+ {
+ if (State == Visibility.Hidden) return false;
+ resume();
+ return true;
+ }
+ return base.OnKeyDown(state, args);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ Alpha = background_alpha,
+ },
+ new FlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FlowDirection.VerticalOnly,
+ Spacing = new Vector2(0f, 50f),
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Children = new Drawable[]
+ {
+ new FlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FlowDirection.VerticalOnly,
+ Spacing = new Vector2(0f, 20f),
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Children = new Drawable[]
+ {
+ new SpriteText
+ {
+ Text = @"paused",
+ Font = @"Exo2.0-Medium",
+ Spacing = new Vector2(5, 0),
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ TextSize = 30,
+ Colour = colours.Yellow,
+ Shadow = true,
+ ShadowColour = new Color4(0, 0, 0, 0.25f)
+ },
+ new SpriteText
+ {
+ Text = @"you're not going to do what i think you're going to do, are ya?",
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Shadow = true,
+ ShadowColour = new Color4(0, 0, 0, 0.25f)
+ }
+ }
+ },
+ new FlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Masking = true,
+ EdgeEffect = new EdgeEffect
+ {
+ Type = EdgeEffectType.Shadow,
+ Colour = Color4.Black.Opacity(0.6f),
+ Radius = 50
+ },
+ Children = new Drawable[]
+ {
+ new ResumeButton
+ {
+ RelativeSizeAxes = Axes.X,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Height = button_height,
+ Action = resume
+ },
+ new RetryButton
+ {
+ RelativeSizeAxes = Axes.X,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Height = button_height,
+ Action = delegate
+ {
+ Hide();
+ OnRetry?.Invoke();
+ }
+ },
+ new QuitButton
+ {
+ RelativeSizeAxes = Axes.X,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Height = button_height,
+ Action = delegate
+ {
+ Hide();
+ OnQuit?.Invoke();
+ }
+ }
+ }
+ },
+ retryCounterContainer = new FlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre
+ }
+ }
+ },
+ new PauseProgressBar
+ {
+ Origin = Anchor.BottomCentre,
+ Anchor = Anchor.BottomCentre,
+ Width = 1f
+ }
+ };
+
+ Retries = 0;
+ }
+
+ private void resume()
+ {
+ Hide();
+ OnResume?.Invoke();
+ }
+
+ public PauseOverlay()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Pause/PauseProgressBar.cs b/osu.Game/Overlays/Pause/PauseProgressBar.cs
new file mode 100644
index 0000000000..28824cb7ea
--- /dev/null
+++ b/osu.Game/Overlays/Pause/PauseProgressBar.cs
@@ -0,0 +1,147 @@
+using OpenTK.Graphics;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Framework.Graphics.Primitives;
+
+namespace osu.Game.Overlays.Pause
+{
+ public class PauseProgressBar : Container
+ {
+ private Color4 fillColour = new Color4(221, 255, 255, 255);
+ private Color4 glowColour = new Color4(221, 255, 255, 150);
+
+ private Container fill;
+ private WorkingBeatmap current;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuGameBase osuGame)
+ {
+ current = osuGame.Beatmap.Value;
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (current?.TrackLoaded ?? false)
+ {
+ fill.Width = (float)(current.Track.CurrentTime / current.Track.Length);
+ }
+ }
+
+ public PauseProgressBar()
+ {
+ RelativeSizeAxes = Axes.X;
+ Height = 60;
+
+ Children = new Drawable[]
+ {
+ new PauseProgressGraph
+ {
+ RelativeSizeAxes = Axes.X,
+ Origin = Anchor.BottomCentre,
+ Anchor = Anchor.BottomCentre,
+ Height = 35,
+ Margin = new MarginPadding
+ {
+ Bottom = 5
+ }
+ },
+ new Container
+ {
+ Origin = Anchor.BottomRight,
+ Anchor = Anchor.BottomRight,
+ RelativeSizeAxes = Axes.X,
+ Height = 5,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ Alpha = 0.5f
+ }
+ }
+ },
+ fill = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Origin = Anchor.BottomLeft,
+ Anchor = Anchor.BottomLeft,
+ Width = 0,
+ Height = 60,
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Origin = Anchor.BottomLeft,
+ Anchor = Anchor.BottomLeft,
+ Masking = true,
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ Origin = Anchor.BottomLeft,
+ Anchor = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.X,
+ Height = 5,
+ Masking = true,
+ EdgeEffect = new EdgeEffect
+ {
+ Type = EdgeEffectType.Glow,
+ Colour = glowColour,
+ Radius = 5
+ },
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = fillColour
+ }
+ }
+ }
+ }
+ },
+ new Container
+ {
+ Origin = Anchor.BottomRight,
+ Anchor = Anchor.BottomRight,
+ Width = 2,
+ Height = 35,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.White
+ },
+ new Container
+ {
+ Origin = Anchor.BottomCentre,
+ Anchor = Anchor.TopCentre,
+ Width = 14,
+ Height = 25,
+ CornerRadius = 5,
+ Masking = true,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.White
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Pause/PauseProgressGraph.cs b/osu.Game/Overlays/Pause/PauseProgressGraph.cs
new file mode 100644
index 0000000000..fab9f37923
--- /dev/null
+++ b/osu.Game/Overlays/Pause/PauseProgressGraph.cs
@@ -0,0 +1,9 @@
+using osu.Framework.Graphics.Containers;
+
+namespace osu.Game.Overlays.Pause
+{
+ public class PauseProgressGraph : Container
+ {
+ // TODO: Implement the pause progress graph
+ }
+}
diff --git a/osu.Game/Overlays/Pause/QuitButton.cs b/osu.Game/Overlays/Pause/QuitButton.cs
new file mode 100644
index 0000000000..05d6171d2e
--- /dev/null
+++ b/osu.Game/Overlays/Pause/QuitButton.cs
@@ -0,0 +1,23 @@
+using OpenTK.Graphics;
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Game.Graphics;
+
+namespace osu.Game.Overlays.Pause
+{
+ public class QuitButton : PauseButton
+ {
+ [BackgroundDependencyLoader]
+ private void load(AudioManager audio, OsuColour colours)
+ {
+ ButtonColour = new Color4(170, 27, 39, 255); // The red from the design isn't in the palette so it's used directly
+ SampleHover = audio.Sample.Get(@"Menu/menuclick");
+ SampleClick = audio.Sample.Get(@"Menu/menuback");
+ }
+
+ public QuitButton()
+ {
+ Text = @"Quit to Main Menu";
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Pause/ResumeButton.cs b/osu.Game/Overlays/Pause/ResumeButton.cs
new file mode 100644
index 0000000000..216a852993
--- /dev/null
+++ b/osu.Game/Overlays/Pause/ResumeButton.cs
@@ -0,0 +1,22 @@
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Game.Graphics;
+
+namespace osu.Game.Overlays.Pause
+{
+ public class ResumeButton : PauseButton
+ {
+ [BackgroundDependencyLoader]
+ private void load(AudioManager audio, OsuColour colours)
+ {
+ ButtonColour = colours.Green;
+ SampleHover = audio.Sample.Get(@"Menu/menuclick");
+ SampleClick = audio.Sample.Get(@"Menu/menuback");
+ }
+
+ public ResumeButton()
+ {
+ Text = @"Continue";
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Pause/RetryButton.cs b/osu.Game/Overlays/Pause/RetryButton.cs
new file mode 100644
index 0000000000..12378ca43b
--- /dev/null
+++ b/osu.Game/Overlays/Pause/RetryButton.cs
@@ -0,0 +1,22 @@
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Game.Graphics;
+
+namespace osu.Game.Overlays.Pause
+{
+ public class RetryButton : PauseButton
+ {
+ [BackgroundDependencyLoader]
+ private void load(AudioManager audio, OsuColour colours)
+ {
+ ButtonColour = colours.YellowDark;
+ SampleHover = audio.Sample.Get(@"Menu/menuclick");
+ SampleClick = audio.Sample.Get(@"Menu/menu-play-click");
+ }
+
+ public RetryButton()
+ {
+ Text = @"Retry";
+ }
+ }
+}
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index d6594dd2be..4fb0d4dfab 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -22,6 +22,7 @@ using osu.Framework.GameModes;
using osu.Game.Modes.UI;
using osu.Game.Screens.Ranking;
using osu.Game.Configuration;
+using osu.Game.Overlays.Pause;
using osu.Framework.Configuration;
using System;
using OpenTK.Graphics;
@@ -39,7 +40,23 @@ namespace osu.Game.Screens.Play
public BeatmapInfo BeatmapInfo;
public PlayMode PreferredPlayMode;
-
+
+ private bool isPaused;
+ public bool IsPaused
+ {
+ get
+ {
+ return isPaused;
+ }
+ }
+
+ public int RestartCount;
+
+ private double pauseCooldown = 1000;
+ private double lastPauseActionTime = 0;
+
+ private bool canPause => Time.Current >= (lastPauseActionTime + pauseCooldown);
+
private IAdjustableClock sourceClock;
private Ruleset ruleset;
@@ -48,6 +65,10 @@ namespace osu.Game.Screens.Play
private HitRenderer hitRenderer;
private Bindable dimLevel;
+ private ScoreOverlay scoreOverlay;
+ private PauseOverlay pauseOverlay;
+ private PlayerInputManager playerInputManager;
+
[BackgroundDependencyLoader]
private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuGameBase game, OsuConfigManager config)
{
@@ -92,9 +113,20 @@ namespace osu.Game.Screens.Play
ruleset = Ruleset.GetRuleset(usablePlayMode);
- var scoreOverlay = ruleset.CreateScoreOverlay();
+ scoreOverlay = ruleset.CreateScoreOverlay();
scoreOverlay.BindProcessor(scoreProcessor = ruleset.CreateScoreProcessor(beatmap.HitObjects.Count));
+ pauseOverlay = new PauseOverlay
+ {
+ Depth = -1,
+ OnResume = delegate {
+ Delay(400);
+ Schedule(Resume);
+ },
+ OnRetry = Restart,
+ OnQuit = Exit
+ };
+
hitRenderer = ruleset.CreateHitRendererWith(beatmap.HitObjects);
//bind HitRenderer to ScoreProcessor and ourselves (for a pass situation)
@@ -109,7 +141,7 @@ namespace osu.Game.Screens.Play
Children = new Drawable[]
{
- new PlayerInputManager(game.Host)
+ playerInputManager = new PlayerInputManager(game.Host)
{
Clock = new InterpolatingFramedClock(sourceClock),
PassThrough = false,
@@ -119,9 +151,62 @@ namespace osu.Game.Screens.Play
}
},
scoreOverlay,
+ pauseOverlay
};
}
+ public void Pause(bool force = false)
+ {
+ if (canPause || force)
+ {
+ lastPauseActionTime = Time.Current;
+ playerInputManager.PassThrough = true;
+ scoreOverlay.KeyCounter.IsCounting = false;
+ pauseOverlay.Retries = RestartCount;
+ pauseOverlay.Show();
+ sourceClock.Stop();
+ isPaused = true;
+ }
+ else
+ {
+ isPaused = false;
+ }
+ }
+
+ public void Resume()
+ {
+ lastPauseActionTime = Time.Current;
+ playerInputManager.PassThrough = false;
+ scoreOverlay.KeyCounter.IsCounting = true;
+ pauseOverlay.Hide();
+ sourceClock.Start();
+ isPaused = false;
+ }
+
+ public void TogglePaused()
+ {
+ isPaused = !IsPaused;
+ if (IsPaused) Pause(); else Resume();
+ }
+
+ public void Restart()
+ {
+ sourceClock.Stop(); // If the clock is running and Restart is called the game will lag until relaunch
+
+ var newPlayer = new Player();
+
+ newPlayer.Preload(Game, delegate
+ {
+ newPlayer.RestartCount = RestartCount + 1;
+ ValidForResume = false;
+
+ if (!Push(newPlayer))
+ {
+ // Error(?)
+ }
+ });
+ }
+
protected override void LoadComplete()
{
base.LoadComplete();
@@ -176,9 +261,19 @@ namespace osu.Game.Screens.Play
protected override bool OnExiting(GameMode next)
{
- dimLevel.ValueChanged -= dimChanged;
- Background?.FadeTo(1f, 200);
- return base.OnExiting(next);
+ if (!canPause) return true;
+
+ if (!IsPaused && sourceClock.IsRunning) // For if the user presses escape quickly when entering the map
+ {
+ Pause();
+ return true;
+ }
+ else
+ {
+ dimLevel.ValueChanged -= dimChanged;
+ Background?.FadeTo(1f, 200);
+ return base.OnExiting(next);
+ }
}
private void dimChanged(object sender, EventArgs e)
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 6e00f895a9..7356889b18 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -237,6 +237,13 @@
+
+
+
+
+
+
+