diff --git a/osu-framework b/osu-framework
index 9d142a8e00..5f19dd913d 160000
--- a/osu-framework
+++ b/osu-framework
@@ -1 +1 @@
-Subproject commit 9d142a8e009794dfee828392e36025d08577131d
+Subproject commit 5f19dd913dfc69013a3b9cf30ccfd9c44881a321
diff --git a/osu.Game/Beatmaps/Timing/BreakPeriod.cs b/osu.Game/Beatmaps/Timing/BreakPeriod.cs
index fb307b7144..0cf4a0c65b 100644
--- a/osu.Game/Beatmaps/Timing/BreakPeriod.cs
+++ b/osu.Game/Beatmaps/Timing/BreakPeriod.cs
@@ -8,7 +8,7 @@ namespace osu.Game.Beatmaps.Timing
///
/// The minimum duration required for a break to have any effect.
///
- private const double min_break_duration = 650;
+ public const double MIN_BREAK_DURATION = 650;
///
/// The break start time.
@@ -28,6 +28,6 @@ namespace osu.Game.Beatmaps.Timing
///
/// Whether the break has any effect. Breaks that are too short are culled before they are added to the beatmap.
///
- public bool HasEffect => Duration >= min_break_duration;
+ public bool HasEffect => Duration >= MIN_BREAK_DURATION;
}
-}
+}
\ No newline at end of file
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index deb87e92d8..0b631a7148 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -51,6 +51,11 @@ namespace osu.Game.Rulesets.Scoring
///
public readonly BindableInt Combo = new BindableInt();
+ ///
+ /// The current rank.
+ ///
+ public readonly Bindable Rank = new Bindable(ScoreRank.X);
+
///
/// THe highest combo achieved by this score.
///
@@ -74,6 +79,7 @@ namespace osu.Game.Rulesets.Scoring
protected ScoreProcessor()
{
Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); };
+ Accuracy.ValueChanged += delegate { Rank.Value = rankFrom(Accuracy.Value); };
}
private ScoreRank rankFrom(double acc)
@@ -101,6 +107,7 @@ namespace osu.Game.Rulesets.Scoring
Accuracy.Value = 1;
Health.Value = 1;
Combo.Value = 0;
+ Rank.Value = ScoreRank.X;
HighestCombo.Value = 0;
alreadyFailed = false;
@@ -142,7 +149,7 @@ namespace osu.Game.Rulesets.Scoring
score.Combo = Combo;
score.MaxCombo = HighestCombo;
score.Accuracy = Accuracy;
- score.Rank = rankFrom(Accuracy);
+ score.Rank = Rank;
score.Date = DateTimeOffset.Now;
score.Health = Health;
}
diff --git a/osu.Game/Screens/Play/BreaksOverlay/ArrowsOverlay.cs b/osu.Game/Screens/Play/BreaksOverlay/ArrowsOverlay.cs
new file mode 100644
index 0000000000..0b775d5c35
--- /dev/null
+++ b/osu.Game/Screens/Play/BreaksOverlay/ArrowsOverlay.cs
@@ -0,0 +1,103 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics;
+using OpenTK;
+using osu.Game.Graphics.Containers;
+using osu.Game.Beatmaps.Timing;
+
+namespace osu.Game.Screens.Play.BreaksOverlay
+{
+ public class ArrowsOverlay : VisibilityContainer
+ {
+ private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
+
+ private const int glow_icon_size = 60;
+ private const int glow_icon_blur_sigma = 10;
+ private const float glow_icon_final_offset = 0.22f;
+ private const float glow_icon_offscreen_offset = 0.6f;
+
+ private const int blurred_icon_blur_sigma = 20;
+ private const int blurred_icon_size = 130;
+ private const float blurred_icon_final_offset = 0.35f;
+ private const float blurred_icon_offscreen_offset = 0.7f;
+
+ private readonly GlowIcon leftGlowIcon;
+ private readonly GlowIcon rightGlowIcon;
+
+ private readonly BlurredIcon leftBlurredIcon;
+ private readonly BlurredIcon rightBlurredIcon;
+
+ public ArrowsOverlay()
+ {
+ RelativeSizeAxes = Axes.Both;
+ Children = new Drawable[]
+ {
+ leftGlowIcon = new GlowIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.CentreRight,
+ X = -glow_icon_offscreen_offset,
+ Icon = Graphics.FontAwesome.fa_chevron_right,
+ BlurSigma = new Vector2(glow_icon_blur_sigma),
+ Size = new Vector2(glow_icon_size),
+ },
+ rightGlowIcon = new GlowIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.CentreLeft,
+ X = glow_icon_offscreen_offset,
+ Icon = Graphics.FontAwesome.fa_chevron_left,
+ BlurSigma = new Vector2(glow_icon_blur_sigma),
+ Size = new Vector2(glow_icon_size),
+ },
+ new ParallaxContainer
+ {
+ ParallaxAmount = -0.02f,
+ Children = new Drawable[]
+ {
+ leftBlurredIcon = new BlurredIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.CentreRight,
+ Alpha = 0.7f,
+ X = -blurred_icon_offscreen_offset,
+ Icon = Graphics.FontAwesome.fa_chevron_right,
+ BlurSigma = new Vector2(blurred_icon_blur_sigma),
+ Size = new Vector2(blurred_icon_size),
+ },
+ rightBlurredIcon = new BlurredIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.CentreLeft,
+ Alpha = 0.7f,
+ X = blurred_icon_offscreen_offset,
+ Icon = Graphics.FontAwesome.fa_chevron_left,
+ BlurSigma = new Vector2(blurred_icon_blur_sigma),
+ Size = new Vector2(blurred_icon_size),
+ },
+ }
+ }
+ };
+ }
+
+ protected override void PopIn()
+ {
+ leftGlowIcon.MoveToX(-glow_icon_final_offset, fade_duration, Easing.OutQuint);
+ rightGlowIcon.MoveToX(glow_icon_final_offset, fade_duration, Easing.OutQuint);
+
+ leftBlurredIcon.MoveToX(-blurred_icon_final_offset, fade_duration, Easing.OutQuint);
+ rightBlurredIcon.MoveToX(blurred_icon_final_offset, fade_duration, Easing.OutQuint);
+ }
+
+ protected override void PopOut()
+ {
+ leftGlowIcon.MoveToX(-glow_icon_offscreen_offset, fade_duration, Easing.OutQuint);
+ rightGlowIcon.MoveToX(glow_icon_offscreen_offset, fade_duration, Easing.OutQuint);
+
+ leftBlurredIcon.MoveToX(-blurred_icon_offscreen_offset, fade_duration, Easing.OutQuint);
+ rightBlurredIcon.MoveToX(blurred_icon_offscreen_offset, fade_duration, Easing.OutQuint);
+ }
+ }
+}
diff --git a/osu.Game/Screens/Play/BreaksOverlay/BlurredIcon.cs b/osu.Game/Screens/Play/BreaksOverlay/BlurredIcon.cs
new file mode 100644
index 0000000000..f16e7c7b96
--- /dev/null
+++ b/osu.Game/Screens/Play/BreaksOverlay/BlurredIcon.cs
@@ -0,0 +1,51 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics;
+using osu.Game.Graphics;
+using osu.Framework.Allocation;
+
+namespace osu.Game.Screens.Play.BreaksOverlay
+{
+ public class BlurredIcon : BufferedContainer
+ {
+ private readonly SpriteIcon icon;
+
+ public FontAwesome Icon
+ {
+ set { icon.Icon = value; }
+ get { return icon.Icon; }
+ }
+
+ public override Vector2 Size
+ {
+ set
+ {
+ icon.Size = value;
+ base.Size = value + BlurSigma * 2.5f;
+ ForceRedraw();
+ }
+ get { return base.Size; }
+ }
+
+ public BlurredIcon()
+ {
+ RelativePositionAxes = Axes.X;
+ CacheDrawnFrameBuffer = true;
+ Child = icon = new SpriteIcon
+ {
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Shadow = false,
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Colour = colours.BlueLighter;
+ }
+ }
+}
diff --git a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs
new file mode 100644
index 0000000000..f5062aa40f
--- /dev/null
+++ b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs
@@ -0,0 +1,151 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Beatmaps.Timing;
+using osu.Game.Rulesets.Scoring;
+using System.Collections.Generic;
+
+namespace osu.Game.Screens.Play.BreaksOverlay
+{
+ public class BreakOverlay : Container
+ {
+ private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
+ private const float remaining_time_container_max_size = 0.3f;
+ private const int vertical_margin = 25;
+
+ private List breaks;
+ public List Breaks
+ {
+ set
+ {
+ breaks = value;
+ initializeBreaks();
+ }
+ get
+ {
+ return breaks;
+ }
+ }
+
+ private readonly bool letterboxing;
+ private readonly LetterboxOverlay letterboxOverlay;
+ private readonly Container remainingTimeAdjustmentBox;
+ private readonly Container remainingTimeBox;
+ private readonly RemainingTimeCounter remainingTimeCounter;
+ private readonly InfoContainer info;
+ private readonly ArrowsOverlay arrowsOverlay;
+
+ public BreakOverlay(bool letterboxing)
+ {
+ this.letterboxing = letterboxing;
+
+ RelativeSizeAxes = Axes.Both;
+ Children = new Drawable[]
+ {
+ letterboxOverlay = new LetterboxOverlay
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ remainingTimeAdjustmentBox = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Width = 0,
+ Child = remainingTimeBox = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.X,
+ Height = 8,
+ CornerRadius = 4,
+ Masking = true,
+ Child = new Box { RelativeSizeAxes = Axes.Both }
+ }
+ },
+ remainingTimeCounter = new RemainingTimeCounter
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.BottomCentre,
+ Margin = new MarginPadding { Bottom = vertical_margin },
+ },
+ info = new InfoContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.TopCentre,
+ Margin = new MarginPadding { Top = vertical_margin },
+ },
+ arrowsOverlay = new ArrowsOverlay
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ }
+ };
+ }
+
+ private void initializeBreaks()
+ {
+ FinishTransforms(true);
+ Scheduler.CancelDelayedTasks();
+
+ if (breaks == null)
+ return;
+
+ foreach (var b in breaks)
+ {
+ if (!b.HasEffect)
+ continue;
+
+ using (BeginAbsoluteSequence(b.StartTime))
+ {
+ Schedule(() => onBreakIn(b));
+ using (BeginDelayedSequence(b.Duration - fade_duration))
+ Schedule(onBreakOut);
+ }
+ }
+ }
+
+ private void onBreakIn(BreakPeriod b)
+ {
+ if (letterboxing)
+ letterboxOverlay.Show();
+
+ remainingTimeAdjustmentBox
+ .ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint)
+ .Delay(b.Duration - fade_duration)
+ .ResizeWidthTo(0);
+
+ remainingTimeBox
+ .ResizeWidthTo(0, b.Duration - fade_duration)
+ .Then()
+ .ResizeWidthTo(1);
+
+ remainingTimeCounter.StartCounting(b.EndTime);
+
+ remainingTimeCounter.Show();
+ info.Show();
+ arrowsOverlay.Show();
+ }
+
+ private void onBreakOut()
+ {
+ if (letterboxing)
+ letterboxOverlay.Hide();
+
+ remainingTimeCounter.Hide();
+ info.Hide();
+ arrowsOverlay.Hide();
+ }
+
+ public void BindProcessor(ScoreProcessor processor)
+ {
+ info.AccuracyDisplay.Current.BindTo(processor.Accuracy);
+ info.GradeDisplay.Current.BindTo(processor.Rank);
+ }
+ }
+}
diff --git a/osu.Game/Screens/Play/BreaksOverlay/GlowIcon.cs b/osu.Game/Screens/Play/BreaksOverlay/GlowIcon.cs
new file mode 100644
index 0000000000..b27eef632c
--- /dev/null
+++ b/osu.Game/Screens/Play/BreaksOverlay/GlowIcon.cs
@@ -0,0 +1,65 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics;
+using osu.Game.Graphics;
+using OpenTK;
+using osu.Framework.Allocation;
+
+namespace osu.Game.Screens.Play.BreaksOverlay
+{
+ public class GlowIcon : Container
+ {
+ private readonly SpriteIcon spriteIcon;
+ private readonly BlurredIcon blurredIcon;
+
+ public override Vector2 Size
+ {
+ set
+ {
+ blurredIcon.Size = spriteIcon.Size = value;
+ blurredIcon.ForceRedraw();
+ }
+ get { return base.Size; }
+ }
+
+ public Vector2 BlurSigma
+ {
+ set { blurredIcon.BlurSigma = value; }
+ get { return blurredIcon.BlurSigma; }
+ }
+
+ public FontAwesome Icon
+ {
+ set { spriteIcon.Icon = blurredIcon.Icon = value; }
+ get { return spriteIcon.Icon; }
+ }
+
+ public GlowIcon()
+ {
+ RelativePositionAxes = Axes.X;
+ AutoSizeAxes = Axes.Both;
+ Children = new Drawable[]
+ {
+ blurredIcon = new BlurredIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ spriteIcon = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Shadow = false,
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ blurredIcon.Colour = colours.Blue;
+ }
+ }
+}
diff --git a/osu.Game/Screens/Play/BreaksOverlay/InfoContainer.cs b/osu.Game/Screens/Play/BreaksOverlay/InfoContainer.cs
new file mode 100644
index 0000000000..1bf9b26cc9
--- /dev/null
+++ b/osu.Game/Screens/Play/BreaksOverlay/InfoContainer.cs
@@ -0,0 +1,58 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Beatmaps.Timing;
+
+namespace osu.Game.Screens.Play.BreaksOverlay
+{
+ public class InfoContainer : VisibilityContainer
+ {
+ private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
+
+ public PercentageInfoLine AccuracyDisplay;
+ public InfoLine RankDisplay;
+ public InfoLine GradeDisplay;
+
+ public InfoContainer()
+ {
+ AutoSizeAxes = Axes.Both;
+ Child = new FillFlowContainer
+ {
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(5),
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Text = "current progress".ToUpper(),
+ TextSize = 15,
+ Font = "Exo2.0-Black",
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Origin = Anchor.TopCentre,
+ Anchor = Anchor.TopCentre,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ AccuracyDisplay = new PercentageInfoLine("Accuracy"),
+ RankDisplay = new InfoLine("Rank"),
+ GradeDisplay = new InfoLine("Grade"),
+ },
+ }
+ },
+ };
+ }
+
+ protected override void PopIn() => this.FadeIn(fade_duration);
+ protected override void PopOut() => this.FadeOut(fade_duration);
+ }
+}
diff --git a/osu.Game/Screens/Play/BreaksOverlay/InfoLine.cs b/osu.Game/Screens/Play/BreaksOverlay/InfoLine.cs
new file mode 100644
index 0000000000..751523d68c
--- /dev/null
+++ b/osu.Game/Screens/Play/BreaksOverlay/InfoLine.cs
@@ -0,0 +1,82 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+
+namespace osu.Game.Screens.Play.BreaksOverlay
+{
+ public class InfoLine : Container
+ where T : struct
+ {
+ private const int margin = 2;
+
+ public Bindable Current = new Bindable();
+
+ private readonly OsuSpriteText text;
+ private readonly OsuSpriteText valueText;
+
+ private readonly string prefix;
+
+ public InfoLine(string name, string prefix = @"")
+ {
+ this.prefix = prefix;
+
+ AutoSizeAxes = Axes.Y;
+ Children = new Drawable[]
+ {
+ text = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.CentreRight,
+ Text = name,
+ TextSize = 17,
+ Margin = new MarginPadding { Right = margin }
+ },
+ valueText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.CentreLeft,
+ Text = prefix + @"-",
+ TextSize = 17,
+ Font = "Exo2.0-Bold",
+ Margin = new MarginPadding { Left = margin }
+ }
+ };
+
+ Current.ValueChanged += currentValueChanged;
+ }
+
+ private void currentValueChanged(T newValue)
+ {
+ var newText = prefix + Format(newValue);
+
+ if (valueText.Text == newText)
+ return;
+
+ valueText.Text = newText;
+ }
+
+ protected virtual string Format(T count) => count.ToString();
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ text.Colour = colours.Yellow;
+ valueText.Colour = colours.YellowLight;
+ }
+ }
+
+ public class PercentageInfoLine : InfoLine
+ {
+ public PercentageInfoLine(string name, string prefix = "") : base(name, prefix)
+ {
+ }
+
+ protected override string Format(double count) => $@"{count:P2}";
+ }
+}
diff --git a/osu.Game/Screens/Play/BreaksOverlay/LetterboxOverlay.cs b/osu.Game/Screens/Play/BreaksOverlay/LetterboxOverlay.cs
new file mode 100644
index 0000000000..9d5bc986e9
--- /dev/null
+++ b/osu.Game/Screens/Play/BreaksOverlay/LetterboxOverlay.cs
@@ -0,0 +1,67 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK.Graphics;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Beatmaps.Timing;
+
+namespace osu.Game.Screens.Play.BreaksOverlay
+{
+ public class LetterboxOverlay : VisibilityContainer
+ {
+ private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
+ private const int height = 350;
+
+ private static readonly Color4 transparent_black = new Color4(0, 0, 0, 0);
+
+ public LetterboxOverlay()
+ {
+ RelativeSizeAxes = Axes.Both;
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ Anchor = Anchor.TopLeft,
+ Origin = Anchor.TopLeft,
+ RelativeSizeAxes = Axes.X,
+ Height = height,
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = new ColourInfo
+ {
+ TopLeft = Color4.Black,
+ TopRight = Color4.Black,
+ BottomLeft = transparent_black,
+ BottomRight = transparent_black,
+ }
+ }
+ },
+ new Container
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.X,
+ Height = height,
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = new ColourInfo
+ {
+ TopLeft = transparent_black,
+ TopRight = transparent_black,
+ BottomLeft = Color4.Black,
+ BottomRight = Color4.Black,
+ }
+ }
+ }
+ };
+ }
+
+ protected override void PopIn() => this.FadeIn(fade_duration);
+ protected override void PopOut() => this.FadeOut(fade_duration);
+ }
+}
diff --git a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs
new file mode 100644
index 0000000000..b5d77d0d02
--- /dev/null
+++ b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs
@@ -0,0 +1,65 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osu.Framework.Graphics;
+using System;
+using osu.Game.Beatmaps.Timing;
+
+namespace osu.Game.Screens.Play.BreaksOverlay
+{
+ public class RemainingTimeCounter : VisibilityContainer
+ {
+ private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
+
+ private readonly OsuSpriteText counter;
+
+ private int? previousSecond;
+
+ private double endTime;
+
+ private bool isCounting;
+
+ public RemainingTimeCounter()
+ {
+ AutoSizeAxes = Axes.Both;
+ Child = counter = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ TextSize = 33,
+ Font = "Venera",
+ };
+ }
+
+ public void StartCounting(double endTime)
+ {
+ this.endTime = endTime;
+ isCounting = true;
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (isCounting)
+ {
+ var currentTime = Clock.CurrentTime;
+ if (currentTime < endTime)
+ {
+ int currentSecond = (int)Math.Ceiling((endTime - Clock.CurrentTime) / 1000.0);
+ if (currentSecond != previousSecond)
+ {
+ counter.Text = currentSecond.ToString();
+ previousSecond = currentSecond;
+ }
+ }
+ else isCounting = false;
+ }
+ }
+
+ protected override void PopIn() => this.FadeIn(fade_duration);
+ protected override void PopOut() => this.FadeOut(fade_duration);
+ }
+}
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index e120c7f193..589f4b663a 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -24,6 +24,7 @@ using osu.Game.Screens.Ranking;
using osu.Framework.Audio.Sample;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
+using osu.Game.Screens.Play.BreaksOverlay;
using osu.Game.Storyboards.Drawables;
using OpenTK.Graphics;
@@ -69,6 +70,7 @@ namespace osu.Game.Screens.Play
#endregion
+ private BreakOverlay breakOverlay;
private Container storyboardContainer;
private DrawableStoryboard storyboard;
@@ -179,16 +181,20 @@ namespace osu.Game.Screens.Play
{
RelativeSizeAxes = Axes.Both,
Clock = offsetClock,
- Children = new Drawable[]
- {
- RulesetContainer,
- }
+ Child = RulesetContainer,
},
hudOverlay = new HUDOverlay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
+ breakOverlay = new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Clock = decoupledClock,
+ Breaks = beatmap.Breaks
+ },
}
},
failOverlay = new FailOverlay
@@ -222,6 +228,8 @@ namespace osu.Game.Screens.Play
hudOverlay.ModDisplay.Current.BindTo(working.Mods);
+ breakOverlay.BindProcessor(scoreProcessor);
+
// Bind ScoreProcessor to ourselves
scoreProcessor.AllJudged += onCompletion;
scoreProcessor.Failed += onFail;
diff --git a/osu.Game/Tests/Visual/TestCaseBreakOverlay.cs b/osu.Game/Tests/Visual/TestCaseBreakOverlay.cs
new file mode 100644
index 0000000000..206ca308cf
--- /dev/null
+++ b/osu.Game/Tests/Visual/TestCaseBreakOverlay.cs
@@ -0,0 +1,91 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Timing;
+using osu.Game.Beatmaps.Timing;
+using osu.Game.Screens.Play.BreaksOverlay;
+using System.Collections.Generic;
+
+namespace osu.Game.Tests.Visual
+{
+ internal class TestCaseBreakOverlay : OsuTestCase
+ {
+ public override string Description => @"Tests breaks behavior";
+
+ private readonly BreakOverlay breakOverlay;
+
+ public TestCaseBreakOverlay()
+ {
+ Clock = new FramedClock();
+
+ Child = breakOverlay = new BreakOverlay(true);
+
+ AddStep("2s break", () => startBreak(2000));
+ AddStep("5s break", () => startBreak(5000));
+ AddStep("10s break", () => startBreak(10000));
+ AddStep("15s break", () => startBreak(15000));
+ AddStep("2s, 2s", startMultipleBreaks);
+ AddStep("0.5s, 0.7s, 1s, 2s", startAnotherMultipleBreaks);
+ }
+
+ private void startBreak(double duration)
+ {
+ breakOverlay.Breaks = new List
+ {
+ new BreakPeriod
+ {
+ StartTime = Clock.CurrentTime,
+ EndTime = Clock.CurrentTime + duration,
+ }
+ };
+ }
+
+ private void startMultipleBreaks()
+ {
+ double currentTime = Clock.CurrentTime;
+
+ breakOverlay.Breaks = new List
+ {
+ new BreakPeriod
+ {
+ StartTime = currentTime,
+ EndTime = currentTime + 2000,
+ },
+ new BreakPeriod
+ {
+ StartTime = currentTime + 4000,
+ EndTime = currentTime + 6000,
+ }
+ };
+ }
+
+ private void startAnotherMultipleBreaks()
+ {
+ double currentTime = Clock.CurrentTime;
+
+ breakOverlay.Breaks = new List
+ {
+ new BreakPeriod // Duration is less than 650 - too short to appear
+ {
+ StartTime = currentTime,
+ EndTime = currentTime + 500,
+ },
+ new BreakPeriod
+ {
+ StartTime = currentTime + 1500,
+ EndTime = currentTime + 2200,
+ },
+ new BreakPeriod
+ {
+ StartTime = currentTime + 3200,
+ EndTime = currentTime + 4200,
+ },
+ new BreakPeriod
+ {
+ StartTime = currentTime + 5200,
+ EndTime = currentTime + 7200,
+ }
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index c8e42a3ad3..728724a080 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -274,6 +274,14 @@
+
+
+
+
+
+
+
+
@@ -736,6 +744,7 @@
+