diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 9e13003c3f..c46b0e3d12 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -110,7 +110,7 @@ namespace osu.Desktop.Overlays // only show a notification if we've previously saved a version to the config file (ie. not the first run). if (!string.IsNullOrEmpty(lastVersion)) - Scheduler.AddDelayed(() => notificationOverlay.Post(new UpdateCompleteNotification(version)), 5000); + notificationOverlay.Post(new UpdateCompleteNotification(version)); } } diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index e4e9807754..4a1798feb4 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -135,8 +135,8 @@ $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + + $(SolutionDir)\packages\OpenTK.3.0.0-git00021\lib\net20\OpenTK.dll True diff --git a/osu.Desktop/packages.config b/osu.Desktop/packages.config index 6b6361b578..e7233a42ac 100644 --- a/osu.Desktop/packages.config +++ b/osu.Desktop/packages.config @@ -6,7 +6,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - + diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index b03c8d2eea..578d8eb34d 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -36,8 +36,8 @@ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + + $(SolutionDir)\packages\OpenTK.3.0.0-git00021\lib\net20\OpenTK.dll True diff --git a/osu.Game.Rulesets.Catch/packages.config b/osu.Game.Rulesets.Catch/packages.config index cde428acea..2369f7529b 100644 --- a/osu.Game.Rulesets.Catch/packages.config +++ b/osu.Game.Rulesets.Catch/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 26181164f9..bdd6656ed9 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -36,8 +36,8 @@ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + + $(SolutionDir)\packages\OpenTK.3.0.0-git00021\lib\net20\OpenTK.dll True diff --git a/osu.Game.Rulesets.Mania/packages.config b/osu.Game.Rulesets.Mania/packages.config index cde428acea..2369f7529b 100644 --- a/osu.Game.Rulesets.Mania/packages.config +++ b/osu.Game.Rulesets.Mania/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index e5387a1ce8..6beb430895 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -117,11 +117,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables progress = Slider.ProgressAt(progress); if (repeat > currentRepeat) - { - if (repeat < Slider.RepeatCount && Ball.Tracking) - PlaySamples(); currentRepeat = repeat; - } //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. if (!InitialCircle.Judgements.Any(j => j.IsHit)) @@ -171,9 +167,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public override Vector2 SelectionPoint => ToScreenSpace(Body.Position); public override Quad SelectionQuad => Body.PathDrawQuad; } - - internal interface ISliderProgress - { - void UpdateProgress(double progress, int repeat); - } } diff --git a/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs new file mode 100644 index 0000000000..cb0d177a60 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Osu.Objects +{ + public interface ISliderProgress + { + void UpdateProgress(double progress, int repeat); + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 5f9f11c783..ec51a10345 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -151,28 +151,22 @@ namespace osu.Game.Rulesets.Osu.Objects private void createRepeatPoints() { - var length = Curve.Distance; - var repeatPointDistance = Math.Min(Distance, length); - var repeatDuration = length / Velocity; + var repeatDuration = Distance / Velocity; for (var repeat = 1; repeat < RepeatCount; repeat++) { - for (var d = repeatPointDistance; d <= length; d += repeatPointDistance) - { - var repeatStartTime = StartTime + repeat * repeatDuration; - var distanceProgress = d / length; + var repeatStartTime = StartTime + repeat * repeatDuration; - AddNested(new RepeatPoint - { - RepeatIndex = repeat, - StartTime = repeatStartTime, - Position = Curve.PositionAt(distanceProgress), - StackHeight = StackHeight, - Scale = Scale, - ComboColour = ComboColour, - Samples = new List(RepeatSamples[repeat]) - }); - } + AddNested(new RepeatPoint + { + RepeatIndex = repeat, + StartTime = repeatStartTime, + Position = Curve.PositionAt(repeat % 2), + StackHeight = StackHeight, + Scale = Scale, + ComboColour = ComboColour, + Samples = new List(RepeatSamples[repeat]) + }); } } } diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs new file mode 100644 index 0000000000..ef0bffa14e --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs @@ -0,0 +1,70 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Tests.Visual; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [Ignore("getting CI working")] + public class TestCaseHitCircle : OsuTestCase + { + private readonly Container content; + protected override Container Content => content; + + private bool auto; + private int depthIndex; + + public TestCaseHitCircle() + { + base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + + AddStep("Single", () => addSingle()); + AddStep("Stream", addStream); + AddToggleStep("Auto", v => auto = v); + } + + private void addSingle(double timeOffset = 0, Vector2? positionOffset = null) + { + positionOffset = positionOffset ?? Vector2.Zero; + + var circle = new HitCircle + { + StartTime = Time.Current + 1000 + timeOffset, + Position = positionOffset.Value + }; + + circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 0 }); + + var drawable = new DrawableHitCircle(circle) + { + Anchor = Anchor.Centre, + Depth = depthIndex++ + }; + + if (auto) + drawable.State.Value = ArmedState.Hit; + + Add(drawable); + } + + private void addStream() + { + Vector2 pos = Vector2.Zero; + + for (int i = 0; i <= 1000; i += 100) + { + addSingle(i, pos); + pos += new Vector2(10); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs deleted file mode 100644 index c4932d7803..0000000000 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Timing; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Tests.Visual; -using OpenTK; - -namespace osu.Game.Rulesets.Osu.Tests -{ - [TestFixture] - [Ignore("getting CI working")] - public class TestCaseHitObjects : OsuTestCase - { - private FramedClock framedClock; - - private bool auto; - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - var rateAdjustClock = new StopwatchClock(true); - framedClock = new FramedClock(rateAdjustClock); - - AddStep(@"circles", () => loadHitobjects(HitObjectType.Circle)); - AddStep(@"slider", () => loadHitobjects(HitObjectType.Slider)); - AddStep(@"spinner", () => loadHitobjects(HitObjectType.Spinner)); - - AddToggleStep("Auto", state => { auto = state; loadHitobjects(mode); }); - AddSliderStep("Playback speed", 0.0, 2.0, 0.5, v => rateAdjustClock.Rate = v); - - framedClock.ProcessFrame(); - - var clockAdjustContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Clock = framedClock, - Children = new[] - { - playfieldContainer = new OsuInputManager(rulesets.GetRuleset(0)) { RelativeSizeAxes = Axes.Both }, - approachContainer = new Container { RelativeSizeAxes = Axes.Both } - } - }; - - Add(clockAdjustContainer); - } - - private HitObjectType mode = HitObjectType.Slider; - - private Container playfieldContainer; - private Container approachContainer; - - private void loadHitobjects(HitObjectType mode) - { - this.mode = mode; - - switch (mode) - { - case HitObjectType.Circle: - const int count = 10; - - for (int i = 0; i < count; i++) - { - var h = new HitCircle - { - StartTime = framedClock.CurrentTime + 600 + i * 80, - Position = new Vector2((i - count / 2) * 14), - }; - - add(new DrawableHitCircle(h)); - } - break; - case HitObjectType.Slider: - add(new DrawableSlider(new Slider - { - StartTime = framedClock.CurrentTime + 600, - ControlPoints = new List - { - new Vector2(-200, 0), - new Vector2(400, 0), - }, - Distance = 400, - Position = new Vector2(-200, 0), - Velocity = 1, - TickDistance = 100, - })); - break; - case HitObjectType.Spinner: - add(new DrawableSpinner(new Spinner - { - StartTime = framedClock.CurrentTime + 600, - EndTime = framedClock.CurrentTime + 1600, - Position = new Vector2(0, 0), - })); - break; - } - } - - private int depth; - - private void add(DrawableOsuHitObject h) - { - h.Anchor = Anchor.Centre; - h.Depth = depth++; - - if (auto) - h.State.Value = ArmedState.Hit; - - playfieldContainer.Add(h); - var proxyable = h as IDrawableHitObjectWithProxiedApproach; - if (proxyable != null) - approachContainer.Add(proxyable.ProxiedLayer.CreateProxy()); - } - - private enum HitObjectType - { - Circle, - Slider, - Spinner - } - } -} diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs new file mode 100644 index 0000000000..7ce9c35bd5 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs @@ -0,0 +1,130 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Tests.Visual; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [Ignore("getting CI working")] + public class TestCaseSlider : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableSlider) }; + + private readonly Container content; + protected override Container Content => content; + + private double speedMultiplier = 2; + private double sliderMultiplier = 2; + private int depthIndex; + + public TestCaseSlider() + { + base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + + AddStep("Single", () => addSingle()); + AddStep("Repeated (1)", () => addRepeated(1)); + AddStep("Repeated (2)", () => addRepeated(2)); + AddStep("Repeated (3)", () => addRepeated(3)); + AddStep("Repeated (4)", () => addRepeated(4)); + AddStep("Stream", addStream); + + AddSliderStep("SpeedMultiplier", 0.01, 10, 2, s => speedMultiplier = s); + AddSliderStep("SliderMultiplier", 0.01, 10, 2, s => sliderMultiplier = s); + } + + private void addSingle(double timeOffset = 0, Vector2? positionOffset = null) + { + positionOffset = positionOffset ?? Vector2.Zero; + + var slider = new Slider + { + StartTime = Time.Current + 1000 + timeOffset, + Position = new Vector2(-200, 0) + positionOffset.Value, + ControlPoints = new List + { + new Vector2(-200, 0) + positionOffset.Value, + new Vector2(400, 0) + positionOffset.Value, + }, + Distance = 400, + }; + + var cpi = new ControlPointInfo(); + cpi.DifficultyPoints.Add(new DifficultyControlPoint { SpeedMultiplier = speedMultiplier }); + + var difficulty = new BeatmapDifficulty + { + SliderMultiplier = (float)sliderMultiplier, + CircleSize = 0 + }; + + slider.ApplyDefaults(cpi, difficulty); + Add(new DrawableSlider(slider) + { + Anchor = Anchor.Centre, + Depth = depthIndex++ + }); + } + + private void addRepeated(int repeats) + { + // The first run through the slider is considered a repeat + repeats++; + + var repeatSamples = new List>(); + for (int i = 0; i < repeats; i++) + repeatSamples.Add(new List()); + + var slider = new Slider + { + StartTime = Time.Current + 1000, + Position = new Vector2(-200, 0), + ControlPoints = new List + { + new Vector2(-200, 0), + new Vector2(400, 0), + }, + Distance = 400, + RepeatCount = repeats, + RepeatSamples = repeatSamples + }; + + var cpi = new ControlPointInfo(); + cpi.DifficultyPoints.Add(new DifficultyControlPoint { SpeedMultiplier = speedMultiplier }); + + var difficulty = new BeatmapDifficulty + { + SliderMultiplier = (float)sliderMultiplier, + CircleSize = 0 + }; + + slider.ApplyDefaults(cpi, difficulty); + Add(new DrawableSlider(slider) + { + Anchor = Anchor.Centre, + Depth = depthIndex++ + }); + } + + private void addStream() + { + Vector2 pos = Vector2.Zero; + + for (int i = 0; i <= 1000; i += 100) + { + addSingle(i, pos); + pos += new Vector2(10); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs new file mode 100644 index 0000000000..76cc70effd --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs @@ -0,0 +1,45 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [Ignore("getting CI working")] + public class TestCaseSpinner : OsuTestCase + { + private readonly Container content; + protected override Container Content => content; + + private int depthIndex; + + public TestCaseSpinner() + { + base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + + AddStep("Single", addSingle); + } + + private void addSingle() + { + var spinner = new Spinner { StartTime = Time.Current + 1000, EndTime = Time.Current + 4000 }; + + spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 0 }); + + var drawable = new DrawableSpinner(spinner) + { + Anchor = Anchor.Centre, + Depth = depthIndex++ + }; + + Add(drawable); + } + } +} diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index ec71869adb..245f3eed91 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -37,8 +37,8 @@ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + + $(SolutionDir)\packages\OpenTK.3.0.0-git00021\lib\net20\OpenTK.dll True @@ -75,6 +75,7 @@ + @@ -86,8 +87,10 @@ - + + + diff --git a/osu.Game.Rulesets.Osu/packages.config b/osu.Game.Rulesets.Osu/packages.config index cde428acea..2369f7529b 100644 --- a/osu.Game.Rulesets.Osu/packages.config +++ b/osu.Game.Rulesets.Osu/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs index 9d0037b97a..982b339d3a 100644 --- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs +++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; -using System.Linq; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Game.Audio; @@ -13,7 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Audio public class DrumSampleMapping { private readonly ControlPointInfo controlPoints; - private readonly Dictionary mappings = new Dictionary(); + private readonly Dictionary mappings = new Dictionary(); public DrumSampleMapping(ControlPointInfo controlPoints, AudioManager audio) { @@ -26,17 +25,17 @@ namespace osu.Game.Rulesets.Taiko.Audio else samplePoints = controlPoints.SamplePoints; - foreach (var s in samplePoints.Distinct()) + foreach (var s in samplePoints) { - mappings[s] = new DrumSample + mappings[s.Time] = new DrumSample { - Centre = s.GetSampleInfo().GetChannel(audio.Sample), - Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample) + Centre = s.GetSampleInfo().GetChannel(audio.Sample, "Taiko"), + Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample, "Taiko") }; } } - public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time)]; + public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time]; public class DrumSample { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 92da3fe09e..cc7dd2fa0f 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -41,6 +41,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // Normal and clap samples are handled by the drum protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != SampleInfo.HIT_NORMAL && s.Name != SampleInfo.HIT_CLAP); + protected override string SampleNamespace => "Taiko"; + protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(); public abstract bool OnPressed(TaikoAction action); diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 5a566fd091..a39d627cc4 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -3,9 +3,6 @@ using osu.Game.Rulesets.Objects.Types; using System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -75,13 +72,7 @@ namespace osu.Game.Rulesets.Taiko.Objects FirstTick = first, TickSpacing = tickSpacing, StartTime = t, - IsStrong = IsStrong, - Samples = new List(Samples.Select(s => new SampleInfo - { - Bank = s.Bank, - Name = @"slidertick", - Volume = s.Volume - })) + IsStrong = IsStrong }); first = false; diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index df1a19267f..c43899ebf1 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Replays { foreach (var tick in drumRoll.NestedHitObjects.OfType()) { - Frames.Add(new ReplayFrame(tick.StartTime, null, null, hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2)); + Frames.Add(new ReplayFrame(tick.StartTime, null, null, hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2)); hitButton = !hitButton; } } diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs b/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs new file mode 100644 index 0000000000..172c6e9d84 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Taiko.Audio; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + [Ignore("getting CI working")] + public class TestCaseInputDrum : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(InputDrum), + typeof(DrumSampleMapping), + typeof(SampleInfo), + typeof(SampleControlPoint) + }; + + public TestCaseInputDrum() + { + Add(new TaikoInputManager(new RulesetInfo { ID = 1 }) + { + RelativeSizeAxes = Axes.Both, + Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(200), + Child = new InputDrum(new ControlPointInfo()) + } + }); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index bf1274256b..9b2ea095d2 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -152,14 +152,14 @@ namespace osu.Game.Rulesets.Taiko.UI target = centreHit; back = centre; - drumSample.Centre.Play(); + drumSample.Centre?.Play(); } else if (action == RimAction) { target = rimHit; back = rim; - drumSample.Rim.Play(); + drumSample.Rim?.Play(); } if (target != null) diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index f1c29c1a34..7e44e85e52 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -36,8 +36,8 @@ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + + $(SolutionDir)\packages\OpenTK.3.0.0-git00021\lib\net20\OpenTK.dll True @@ -83,6 +83,7 @@ + diff --git a/osu.Game.Rulesets.Taiko/packages.config b/osu.Game.Rulesets.Taiko/packages.config index cde428acea..2369f7529b 100644 --- a/osu.Game.Rulesets.Taiko/packages.config +++ b/osu.Game.Rulesets.Taiko/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs index 0168cedc86..3a50e43239 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs @@ -1,69 +1,161 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; +using System.Collections.Generic; +using System.Linq; using OpenTK; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Select; +using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { public class TestCaseBeatmapInfoWedge : OsuTestCase { - private BeatmapManager beatmaps; - private readonly Random random; - private readonly BeatmapInfoWedge infoWedge; + private RulesetStore rulesets; + private TestBeatmapInfoWedge infoWedge; + private readonly List beatmaps = new List(); private readonly Bindable beatmap = new Bindable(); - public TestCaseBeatmapInfoWedge() + [BackgroundDependencyLoader] + private void load(OsuGameBase game, RulesetStore rulesets) { - random = new Random(0123); + this.rulesets = rulesets; - Add(infoWedge = new BeatmapInfoWedge + beatmap.BindTo(game.Beatmap); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Add(infoWedge = new TestBeatmapInfoWedge { Size = new Vector2(0.5f, 245), RelativeSizeAxes = Axes.X, - Margin = new MarginPadding - { - Top = 20, - }, + Margin = new MarginPadding { Top = 20 } }); AddStep("show", () => { - Content.FadeInFromZero(250); infoWedge.State = Visibility.Visible; infoWedge.UpdateBeatmap(beatmap); }); - AddStep("hide", () => + + AddWaitStep(3); + + AddStep("hide", () => { infoWedge.State = Visibility.Hidden; }); + + AddWaitStep(3); + + AddStep("show", () => { infoWedge.State = Visibility.Visible; }); + + foreach (var rulesetInfo in rulesets.AvailableRulesets) { - infoWedge.State = Visibility.Hidden; - Content.FadeOut(100); + var ruleset = rulesetInfo.CreateInstance(); + beatmaps.Add(createTestBeatmap(rulesetInfo)); + + var name = rulesetInfo.ShortName; + selectBeatmap(name); + + // TODO: adjust cases once more info is shown for other gamemodes + switch (ruleset) + { + case OsuRuleset osu: + testOsuBeatmap(osu); + testInfoLabels(5); + break; + default: + testInfoLabels(2); + break; + } + } + + testNullBeatmap(); + } + + private void testOsuBeatmap(OsuRuleset ruleset) + { + AddAssert("check version", () => infoWedge.Info.VersionLabel.Text == $"{ruleset.ShortName}Version"); + AddAssert("check title", () => infoWedge.Info.TitleLabel.Text == $"{ruleset.ShortName}Source — {ruleset.ShortName}Title"); + AddAssert("check artist", () => infoWedge.Info.ArtistLabel.Text == $"{ruleset.ShortName}Artist"); + AddAssert("check author", () => infoWedge.Info.MapperContainer.Children.OfType().Any(s => s.Text == $"{ruleset.ShortName}Author")); + } + + private void testInfoLabels(int expectedCount) + { + AddAssert("check infolabels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any()); + AddAssert("check infolabels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount); + } + + private void testNullBeatmap() + { + selectNullBeatmap(); + AddAssert("check empty version", () => string.IsNullOrEmpty(infoWedge.Info.VersionLabel.Text)); + AddAssert("check default title", () => infoWedge.Info.TitleLabel.Text == beatmap.Default.BeatmapInfo.Metadata.Title); + AddAssert("check default artist", () => infoWedge.Info.ArtistLabel.Text == beatmap.Default.BeatmapInfo.Metadata.Artist); + AddAssert("check empty author", () => !infoWedge.Info.MapperContainer.Children.Any()); + AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any()); + } + + private void selectBeatmap(string name) + { + var infoBefore = infoWedge.Info; + + AddStep($"select {name} beatmap", () => + { + beatmap.Value = new TestWorkingBeatmap(beatmaps.First(b => b.BeatmapInfo.Ruleset.ShortName == name)); + infoWedge.UpdateBeatmap(beatmap); }); - AddStep("random beatmap", randomBeatmap); - AddStep("null beatmap", () => infoWedge.UpdateBeatmap(beatmap.Default)); + + AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load"); } - [BackgroundDependencyLoader] - private void load(OsuGameBase game, BeatmapManager beatmaps) + private void selectNullBeatmap() { - this.beatmaps = beatmaps; - beatmap.BindTo(game.Beatmap); + AddStep("select null beatmap", () => + { + beatmap.Value = beatmap.Default; + infoWedge.UpdateBeatmap(beatmap); + }); } - private void randomBeatmap() + private Beatmap createTestBeatmap(RulesetInfo ruleset) { - var sets = beatmaps.GetAllUsableBeatmapSets(); - if (sets.Count == 0) - return; + List objects = new List(); + for (double i = 0; i < 50000; i += 1000) + objects.Add(new HitObject { StartTime = i }); - var b = sets[random.Next(0, sets.Count)].Beatmaps[0]; - beatmap.Value = beatmaps.GetWorkingBeatmap(b); - infoWedge.UpdateBeatmap(beatmap); + return new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + AuthorString = $"{ruleset.ShortName}Author", + Artist = $"{ruleset.ShortName}Artist", + Source = $"{ruleset.ShortName}Source", + Title = $"{ruleset.ShortName}Title" + }, + Ruleset = ruleset, + StarDifficulty = 6, + Version = $"{ruleset.ShortName}Version" + }, + HitObjects = objects + }; + } + + private class TestBeatmapInfoWedge : BeatmapInfoWedge + { + public new BufferedWedgeInfo Info => base.Info; } } } diff --git a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs index 233914767d..46deca073f 100644 --- a/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseNotificationOverlay.cs @@ -1,9 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; using osu.Game.Overlays; @@ -16,6 +18,16 @@ namespace osu.Game.Tests.Visual private readonly NotificationOverlay manager; private readonly List progressingNotifications = new List(); + public override IReadOnlyList RequiredTypes => new[] + { + typeof(NotificationSection), + typeof(SimpleNotification), + typeof(ProgressNotification), + typeof(ProgressCompletionNotification), + typeof(IHasCompletionTarget), + typeof(Notification) + }; + public TestCaseNotificationOverlay() { progressingNotifications.Clear(); @@ -30,17 +42,44 @@ namespace osu.Game.Tests.Visual Content.Add(displayedCount); + void setState(Visibility state) => AddStep(state.ToString(), () => manager.State = state); + void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected); + manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count}"; }; - AddStep(@"toggle", manager.ToggleVisibility); + + setState(Visibility.Visible); AddStep(@"simple #1", sendHelloNotification); AddStep(@"simple #2", sendAmazingNotification); AddStep(@"progress #1", sendUploadProgress); AddStep(@"progress #2", sendDownloadProgress); - AddStep(@"barrage", () => sendBarrage()); + + checkProgressingCount(2); + + setState(Visibility.Hidden); + + AddRepeatStep(@"add many simple", sendManyNotifications, 3); + AddWaitStep(5); + + checkProgressingCount(0); + + AddStep(@"progress #3", sendUploadProgress); + + checkProgressingCount(1); + + AddAssert("Displayed count is 33", () => manager.UnreadCount.Value == 33); + + AddWaitStep(10); + + checkProgressingCount(0); + + + setState(Visibility.Visible); + + //AddStep(@"barrage", () => sendBarrage()); } - private void sendBarrage(int remaining = 100) + private void sendBarrage(int remaining = 10) { switch (RNG.Next(0, 4)) { @@ -70,7 +109,7 @@ namespace osu.Game.Tests.Visual if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3) { - var p = progressingNotifications.FirstOrDefault(n => n.IsAlive && n.State == ProgressNotificationState.Queued); + var p = progressingNotifications.FirstOrDefault(n => n.State == ProgressNotificationState.Queued); if (p != null) p.State = ProgressNotificationState.Active; } @@ -78,7 +117,7 @@ namespace osu.Game.Tests.Visual foreach (var n in progressingNotifications.FindAll(n => n.State == ProgressNotificationState.Active)) { if (n.Progress < 1) - n.Progress += (float)(Time.Elapsed / 2000) * RNG.NextSingle(); + n.Progress += (float)(Time.Elapsed / 400) * RNG.NextSingle(); else n.State = ProgressNotificationState.Completed; } @@ -115,5 +154,11 @@ namespace osu.Game.Tests.Visual { manager.Post(new SimpleNotification { Text = @"Welcome to osu!. Enjoy your stay!" }); } + + private void sendManyNotifications() + { + for (int i = 0; i < 10; i++) + manager.Post(new SimpleNotification { Text = @"Spam incoming!!" }); + } } } diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs index 6435df7c2c..18e40db064 100644 --- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs @@ -26,6 +26,7 @@ namespace osu.Game.Tests.Visual private RulesetStore rulesets; private DependencyContainer dependencies; + private WorkingBeatmap defaultBeatmap; public override IReadOnlyList RequiredTypes => new[] { @@ -47,31 +48,61 @@ namespace osu.Game.Tests.Visual protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent); + private class TestSongSelect : PlaySongSelect + { + public WorkingBeatmap CurrentBeatmap => Beatmap.Value; + public new BeatmapCarousel Carousel => base.Carousel; + } + [BackgroundDependencyLoader] private void load(BeatmapManager baseManager) { - PlaySongSelect songSelect; + TestSongSelect songSelect = null; - if (manager == null) + var storage = new TestStorage(@"TestCasePlaySongSelect"); + + // this is by no means clean. should be replacing inside of OsuGameBase somehow. + var context = new OsuDbContext(); + + Func contextFactory = () => context; + + dependencies.Cache(rulesets = new RulesetStore(contextFactory)); + dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null) { - var storage = new TestStorage(@"TestCasePlaySongSelect"); + DefaultBeatmap = defaultBeatmap = baseManager.GetWorkingBeatmap(null) + }); - // this is by no means clean. should be replacing inside of OsuGameBase somehow. - var context = new OsuDbContext(); + void loadNewSongSelect(bool deleteMaps = false) => AddStep("reload song select", () => + { + if (deleteMaps) manager.DeleteAll(); - Func contextFactory = () => context; - - dependencies.Cache(rulesets = new RulesetStore(contextFactory)); - dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null) + if (songSelect != null) { - DefaultBeatmap = baseManager.GetWorkingBeatmap(null) - }); + Remove(songSelect); + songSelect.Dispose(); + } + Add(songSelect = new TestSongSelect()); + }); + + loadNewSongSelect(true); + + AddWaitStep(3); + + AddAssert("dummy selected", () => songSelect.CurrentBeatmap == defaultBeatmap); + + AddStep("import test maps", () => + { for (int i = 0; i < 100; i += 10) manager.Import(createTestBeatmapSet(i)); - } + }); - Add(songSelect = new PlaySongSelect()); + AddWaitStep(3); + AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap); + + loadNewSongSelect(); + AddWaitStep(3); + AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap); AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; }); AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; }); diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index e0b9eb4091..df66896d9b 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -37,8 +37,8 @@ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + + $(SolutionDir)\packages\OpenTK.3.0.0-git00021\lib\net20\OpenTK.dll True diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config index e09f2a07ba..c2056e09a8 100644 --- a/osu.Game.Tests/packages.config +++ b/osu.Game.Tests/packages.config @@ -6,6 +6,6 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - + \ No newline at end of file diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index 64a9aa50a0..71975bf0fa 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -14,10 +14,20 @@ namespace osu.Game.Audio public const string HIT_NORMAL = @"hitnormal"; public const string HIT_CLAP = @"hitclap"; - public SampleChannel GetChannel(SampleManager manager) + public SampleChannel GetChannel(SampleManager manager, string resourceNamespace = null) { - var channel = manager.Get($"Gameplay/{Bank}-{Name}"); - channel.Volume.Value = Volume / 100.0; + SampleChannel channel = null; + + if (resourceNamespace != null) + channel = manager.Get($"Gameplay/{resourceNamespace}/{Bank}-{Name}"); + + // try without namespace as a fallback. + if (channel == null) + channel = manager.Get($"Gameplay/{Bank}-{Name}"); + + if (channel != null) + channel.Volume.Value = Volume / 100.0; + return channel; } diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index 40e45da13c..c2c13e1909 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -17,7 +17,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The default sample volume at this control point. /// - public int SampleVolume; + public int SampleVolume = 100; /// /// Create a SampleInfo based on the sample settings in this control point. diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 1434943da0..3ec83ed8d5 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -21,8 +21,7 @@ namespace osu.Game.Beatmaps Metadata = new BeatmapMetadata { Artist = "please load a beatmap!", - Title = "no beatmaps available!", - AuthorString = "no one", + Title = "no beatmaps available!" }, BeatmapSet = new BeatmapSetInfo(), BaseDifficulty = new BeatmapDifficulty diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index fe7ca77d44..2bc32794d7 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -16,6 +16,7 @@ using osu.Game.Screens; using osu.Game.Screens.Menu; using OpenTK; using System.Linq; +using System.Threading; using System.Threading.Tasks; using osu.Framework.Input.Bindings; using osu.Framework.Platform; @@ -64,6 +65,8 @@ namespace osu.Game public float ToolbarOffset => Toolbar.Position.Y + Toolbar.DrawHeight; + public readonly BindableBool ShowOverlays = new BindableBool(); + private OsuScreen screenStack; private VolumeControl volume; @@ -220,15 +223,7 @@ namespace osu.Game Depth = -6, }, overlayContent.Add); - Logger.NewEntry += entry => - { - if (entry.Level < LogLevel.Important) return; - - notifications.Post(new SimpleNotification - { - Text = $@"{entry.Level}: {entry.Message}" - }); - }; + forwardLoggedErrorsToNotifications(); dependencies.Cache(settings); dependencies.Cache(social); @@ -287,9 +282,54 @@ namespace osu.Game settings.StateChanged += _ => updateScreenOffset(); notifications.StateChanged += _ => updateScreenOffset(); + notifications.Enabled.BindTo(ShowOverlays); + + ShowOverlays.ValueChanged += visible => + { + //central game screen change logic. + if (!visible) + { + hideAllOverlays(); + musicController.State = Visibility.Hidden; + Toolbar.State = Visibility.Hidden; + } + else + Toolbar.State = Visibility.Visible; + }; + Cursor.State = Visibility.Hidden; } + private void forwardLoggedErrorsToNotifications() + { + int recentErrorCount = 0; + + const double debounce = 5000; + + Logger.NewEntry += entry => + { + if (entry.Level < LogLevel.Error || entry.Target == null) return; + + if (recentErrorCount < 2) + { + notifications.Post(new SimpleNotification + { + Icon = FontAwesome.fa_bomb, + Text = (recentErrorCount == 0 ? entry.Message : "Subsequent errors occurred and have been logged.") + "\nClick to view log files.", + Activated = () => + { + Host.Storage.GetStorageForDirectory("logs").OpenInNativeExplorer(); + return true; + } + }); + } + + Interlocked.Increment(ref recentErrorCount); + + Scheduler.AddDelayed(() => Interlocked.Decrement(ref recentErrorCount), debounce); + }; + } + private Task asyncLoadStream; private void loadComponentSingleFile(T d, Action add) @@ -338,8 +378,6 @@ namespace osu.Game public bool OnReleased(GlobalAction action) => false; - public event Action ScreenChanged; - private Container mainContent; private Container overlayContent; @@ -357,29 +395,6 @@ namespace osu.Game notifications.State = Visibility.Hidden; } - private void screenChanged(Screen newScreen) - { - currentScreen = newScreen as OsuScreen; - - if (currentScreen == null) - { - Exit(); - return; - } - - //central game screen change logic. - if (!currentScreen.ShowOverlays) - { - hideAllOverlays(); - musicController.State = Visibility.Hidden; - Toolbar.State = Visibility.Hidden; - } - else - Toolbar.State = Visibility.Visible; - - ScreenChanged?.Invoke(newScreen); - } - protected override bool OnExiting() { if (screenStack.ChildScreen == null) return false; @@ -425,15 +440,18 @@ namespace osu.Game private void screenAdded(Screen newScreen) { + currentScreen = (OsuScreen)newScreen; + newScreen.ModePushed += screenAdded; newScreen.Exited += screenRemoved; - - screenChanged(newScreen); } private void screenRemoved(Screen newScreen) { - screenChanged(newScreen); + currentScreen = (OsuScreen)newScreen; + + if (newScreen == null) + Exit(); } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 0ddff5e5aa..ea0bf22112 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -177,8 +177,7 @@ namespace osu.Game } catch (MigrationFailedException e) { - Logger.Log((e.InnerException ?? e).ToString(), LoggingTarget.Database, LogLevel.Error); - Logger.Log("Migration failed! We'll be starting with a fresh database.", LoggingTarget.Database, LogLevel.Error); + Logger.Error(e.InnerException ?? e, "Migration failed! We'll be starting with a fresh database.", LoggingTarget.Database); // if we failed, let's delete the database and start fresh. // todo: we probably want a better (non-destructive) migrations/recovery process at a later point than this. diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 2f1c3285ef..5744e8c189 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Linq; -using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -11,7 +10,9 @@ using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; using System; +using osu.Framework.Allocation; using osu.Framework.Configuration; +using osu.Framework.Threading; namespace osu.Game.Overlays { @@ -21,6 +22,11 @@ namespace osu.Game.Overlays public const float TRANSITION_LENGTH = 600; + /// + /// Whether posted notifications should be processed. + /// + public readonly BindableBool Enabled = new BindableBool(true); + private FlowContainer sections; /// @@ -28,6 +34,27 @@ namespace osu.Game.Overlays /// public Func GetToolbarHeight; + public NotificationOverlay() + { + ScheduledDelegate notificationsEnabler = null; + Enabled.ValueChanged += v => + { + if (!IsLoaded) + { + processingPosts = v; + return; + } + + notificationsEnabler?.Cancel(); + + if (v) + // we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed. + notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, 1000); + else + processingPosts = false; + }; + } + [BackgroundDependencyLoader] private void load() { @@ -85,14 +112,21 @@ namespace osu.Game.Overlays private void notificationClosed() { - // hide ourselves if all notifications have been dismissed. - if (totalCount == 0) - State = Visibility.Hidden; + Schedule(() => + { + // hide ourselves if all notifications have been dismissed. + if (totalCount == 0) + State = Visibility.Hidden; + }); updateCounts(); } - public void Post(Notification notification) => Schedule(() => + private readonly Scheduler postScheduler = new Scheduler(); + + private bool processingPosts = true; + + public void Post(Notification notification) => postScheduler.Add(() => { ++runningDepth; notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth; @@ -109,6 +143,13 @@ namespace osu.Game.Overlays updateCounts(); }); + protected override void Update() + { + base.Update(); + if (processingPosts) + postScheduler.Update(); + } + protected override void PopIn() { base.PopIn(); diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index b4720e79c4..dc2dcf2d74 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -91,7 +91,6 @@ namespace osu.Game.Overlays.Notifications AutoSizeAxes = Axes.Y, Padding = new MarginPadding { - Top = 5, Left = 45, Right = 30 }, diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index 2bd0321d12..42fcc3aa0f 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -15,7 +15,7 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Notifications { - public class NotificationSection : FillFlowContainer + public class NotificationSection : AlwaysUpdateFillFlowContainer { private OsuSpriteText titleText; private OsuSpriteText countText; @@ -33,6 +33,7 @@ namespace osu.Game.Overlays.Notifications public IEnumerable AcceptTypes; private string clearText; + public string ClearText { get { return clearText; } @@ -110,7 +111,7 @@ namespace osu.Game.Overlays.Notifications }, }, }, - notifications = new FillFlowContainer + notifications = new AlwaysUpdateFillFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, @@ -159,4 +160,13 @@ namespace osu.Game.Overlays.Notifications notifications?.Children.ForEach(n => n.Read = true); } } + + public class AlwaysUpdateFillFlowContainer : FillFlowContainer + where T : Drawable + { + // this is required to ensure correct layout and scheduling on children. + // the layout portion of this is being tracked as a framework issue (https://github.com/ppy/osu-framework/issues/1297). + protected override bool RequiresChildrenUpdate => true; + } + } diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index c39c630858..d797372390 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -95,8 +95,8 @@ namespace osu.Game.Overlays.Notifications protected virtual void Completed() { - base.Close(); CompletionTarget?.Invoke(CreateCompletionNotification()); + base.Close(); } public override bool DisplayOnTop => false; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 4487f74364..36740b96cb 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Edit } catch (Exception e) { - Logger.Log($"Could not load this beatmap sucessfully ({e})!", LoggingTarget.Runtime, LogLevel.Error); + Logger.Error(e, "Could not load beatmap sucessfully!"); return; } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index ca5326f35e..af525903c5 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -74,6 +74,9 @@ namespace osu.Game.Rulesets.Objects.Drawables protected List Samples = new List(); protected virtual IEnumerable GetSamples() => HitObject.Samples; + // Todo: Rulesets should be overriding the resources instead, but we need to figure out where/when to apply overrides first + protected virtual string SampleNamespace => null; + public readonly Bindable State = new Bindable(); protected DrawableHitObject(TObject hitObject) @@ -101,7 +104,7 @@ namespace osu.Game.Rulesets.Objects.Drawables Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume }; - SampleChannel channel = localSampleInfo.GetChannel(audio.Sample); + SampleChannel channel = localSampleInfo.GetChannel(audio.Sample, SampleNamespace); if (channel == null) continue; diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 5cdf46ee46..3038c51e64 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -38,6 +38,13 @@ namespace osu.Game.Rulesets /// A ruleset, if available, else null. public RulesetInfo GetRuleset(int id) => AvailableRulesets.FirstOrDefault(r => r.ID == id); + /// + /// Retrieve a ruleset using a known short name. + /// + /// The ruleset's short name. + /// A ruleset, if available, else null. + public RulesetInfo GetRuleset(string shortName) => AvailableRulesets.FirstOrDefault(r => r.ShortName == shortName); + /// /// All available rulesets. /// diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs index 367cf4337d..b2308aca71 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -47,6 +47,8 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts if (Beatmap.Value == null) return; + if (Beatmap.Value.Track.Length == double.PositiveInfinity) return; + float markerPos = MathHelper.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth); seekTo(markerPos / DrawWidth * Beatmap.Value.Track.Length); } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 19d00f3477..76f51d1c33 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Edit { protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); - public override bool ShowOverlays => false; + public override bool ShowOverlaysOnEnter => false; private readonly Box bottomBackground; private readonly Container screenContainer; diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index ec2e8e0cb1..c96194f63d 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens { private bool showDisclaimer; - public override bool ShowOverlays => false; + public override bool ShowOverlaysOnEnter => false; public Loader() { diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index ce7856c5a9..c82d90d16c 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -11,12 +11,12 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Game.Graphics; -using osu.Game.Overlays.Toolbar; using OpenTK; using OpenTK.Graphics; using OpenTK.Input; using osu.Framework.Audio.Sample; using osu.Framework.Audio; +using osu.Framework.Configuration; using osu.Framework.Threading; namespace osu.Game.Screens.Menu @@ -25,6 +25,8 @@ namespace osu.Game.Screens.Menu { public event Action StateChanged; + private readonly BindableBool showOverlays = new BindableBool(); + public Action OnEdit; public Action OnExit; public Action OnDirect; @@ -34,8 +36,6 @@ namespace osu.Game.Screens.Menu public Action OnChart; public Action OnTest; - private Toolbar toolbar; - private readonly FlowContainerWithOrigin buttonFlow; //todo: make these non-internal somehow. @@ -131,9 +131,9 @@ namespace osu.Game.Screens.Menu } [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, OsuGame game = null) + private void load(AudioManager audio, OsuGame game) { - toolbar = game?.Toolbar; + if (game != null) showOverlays.BindTo(game.ShowOverlays); sampleBack = audio.Sample.Get(@"Menu/button-back-select"); } @@ -300,7 +300,7 @@ namespace osu.Game.Screens.Menu logoDelayedAction = Scheduler.AddDelayed(() => { - toolbar?.Hide(); + showOverlays.Value = false; logo.ClearTransforms(targetMember: nameof(Position)); logo.RelativePositionAxes = Axes.Both; @@ -329,7 +329,7 @@ namespace osu.Game.Screens.Menu logoTracking = true; logo.Impact(); - toolbar?.Show(); + showOverlays.Value = true; }, 200); break; default: diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 532ee71b72..d0ad613640 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Menu private readonly SpriteIcon icon; private Color4 iconColour; - public override bool ShowOverlays => false; + public override bool ShowOverlaysOnEnter => false; public override bool HasLocalCursorDisplayed => true; diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index d7beb34a2f..a6a1afa320 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Menu public override bool HasLocalCursorDisplayed => true; - public override bool ShowOverlays => false; + public override bool ShowOverlaysOnEnter => false; protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty(); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 90f68ba9f1..fac0ec1422 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Menu { private readonly ButtonSystem buttons; - public override bool ShowOverlays => buttons.State != MenuState.Initial; + public override bool ShowOverlaysOnEnter => buttons.State != MenuState.Initial; private readonly BackgroundScreenDefault background; private Screen songSelect; diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 4a27c7f1ea..0013d1a882 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -28,7 +28,12 @@ namespace osu.Game.Screens /// protected virtual BackgroundScreen CreateBackground() => null; - public virtual bool ShowOverlays => true; + protected BindableBool ShowOverlays = new BindableBool(); + + /// + /// Whether overlays should be shown when this screen is entered or resumed. + /// + public virtual bool ShowOverlaysOnEnter => true; protected new OsuGameBase Game => base.Game as OsuGameBase; @@ -70,7 +75,10 @@ namespace osu.Game.Screens } if (osuGame != null) + { Ruleset.BindTo(osuGame.Ruleset); + ShowOverlays.BindTo(osuGame.ShowOverlays); + } sampleExit = audio.Sample.Get(@"UI/screen-back"); } @@ -94,6 +102,8 @@ namespace osu.Game.Screens base.OnResuming(last); logo.AppendAnimatingAction(() => LogoArriving(logo, true), true); sampleExit?.Play(); + + ShowOverlays.Value = ShowOverlaysOnEnter; } protected override void OnSuspending(Screen next) @@ -139,6 +149,8 @@ namespace osu.Game.Screens logo.AppendAnimatingAction(() => LogoArriving(logo, false), true); base.OnEntering(last); + + ShowOverlays.Value = ShowOverlaysOnEnter; } protected override bool OnExiting(Screen next) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 340fc39d52..35f39e940f 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Play { protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); - public override bool ShowOverlays => false; + public override bool ShowOverlaysOnEnter => false; public override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && RulesetContainer.ProvidingUserCursor; @@ -46,6 +46,8 @@ namespace osu.Game.Screens.Play public bool HasFailed { get; private set; } public bool AllowPause { get; set; } = true; + public bool AllowLeadIn { get; set; } = true; + public bool AllowResults { get; set; } = true; public int RestartCount; @@ -125,7 +127,7 @@ namespace osu.Game.Screens.Play } catch (Exception e) { - Logger.Log($"Could not load this beatmap sucessfully ({e})!", LoggingTarget.Runtime, LogLevel.Error); + Logger.Error(e, "Could not load beatmap sucessfully!"); //couldn't load, hard abort! Exit(); @@ -136,7 +138,10 @@ namespace osu.Game.Screens.Play decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; var firstObjectTime = RulesetContainer.Objects.First().StartTime; - decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn))); + decoupledClock.Seek(AllowLeadIn + ? Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn)) + : firstObjectTime); + decoupledClock.ProcessFrame(); offsetClock = new FramedOffsetClock(decoupledClock); @@ -273,6 +278,8 @@ namespace osu.Game.Screens.Play ValidForResume = false; + if (!AllowResults) return; + using (BeginDelayedSequence(1000)) { onCompletionEvent = Schedule(delegate diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index de67bef004..15a97096e7 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Play private BeatmapMetadataDisplay info; private bool showOverlays = true; - public override bool ShowOverlays => showOverlays; + public override bool ShowOverlaysOnEnter => showOverlays; public override bool AllowBeatmapRulesetChange => false; @@ -250,7 +250,7 @@ namespace osu.Game.Screens.Play Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, }, - new MetadataLine("Mapper", metadata.Author.Username) + new MetadataLine("Mapper", metadata.AuthorString) { Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs index 4c776aa7ab..25a42cae1c 100644 --- a/osu.Game/Screens/Ranking/ResultsPageScore.cs +++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs @@ -324,7 +324,14 @@ namespace osu.Game.Screens.Ranking title.Colour = artist.Colour = colours.BlueDarker; versionMapper.Colour = colours.Gray8; - versionMapper.Text = $"{beatmap.Version} - mapped by {beatmap.Metadata.Author.Username}"; + var creator = beatmap.Metadata.Author?.Username; + if (!string.IsNullOrEmpty(creator)) { + versionMapper.Text = $"mapped by {creator}"; + + if (!string.IsNullOrEmpty(beatmap.Version)) + versionMapper.Text = $"{beatmap.Version} - " + versionMapper.Text; + } + title.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title); artist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist); } diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 87edfe9692..e877633ab3 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -53,6 +53,11 @@ namespace osu.Game.Screens.Select public override bool HandleInput => AllowSelection; + /// + /// Used to avoid firing null selections before the initial beatmaps have been loaded via . + /// + private bool initialLoadComplete; + private IEnumerable beatmapSets => root.Children.OfType(); public IEnumerable BeatmapSets @@ -75,7 +80,12 @@ namespace osu.Game.Screens.Select scrollableContent.Clear(false); itemsCache.Invalidate(); scrollPositionCache.Invalidate(); - BeatmapSetsChanged?.Invoke(); + + Schedule(() => + { + BeatmapSetsChanged?.Invoke(); + initialLoadComplete = true; + }); })); } } @@ -154,6 +164,7 @@ namespace osu.Game.Screens.Select select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.Beatmap.ID == selectedBeatmap?.Beatmap.ID) ?? newSet); itemsCache.Invalidate(); + Schedule(() => BeatmapSetsChanged?.Invoke()); }); } @@ -511,7 +522,7 @@ namespace osu.Game.Screens.Select currentY += DrawHeight / 2; scrollableContent.Height = currentY; - if (selectedBeatmapSet != null && (selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected)) + if (initialLoadComplete && (selectedBeatmapSet == null || selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected)) { selectedBeatmapSet = null; SelectionChanged?.Invoke(null); diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 3ef6ceeaeb..729cb458c2 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Select { private static readonly Vector2 wedged_container_shear = new Vector2(0.15f, 0); - private Drawable info; + protected BufferedWedgeInfo Info; public BeatmapInfoWedge() { @@ -35,6 +35,7 @@ namespace osu.Game.Screens.Select Masking = true; BorderColour = new Color4(221, 255, 255, 255); BorderThickness = 2.5f; + Alpha = 0; EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, @@ -50,12 +51,14 @@ namespace osu.Game.Screens.Select { this.MoveToX(0, 800, Easing.OutQuint); this.RotateTo(0, 800, Easing.OutQuint); + this.FadeIn(250); } protected override void PopOut() { - this.MoveToX(-100, 800, Easing.InQuint); - this.RotateTo(10, 800, Easing.InQuint); + this.MoveToX(-100, 800, Easing.In); + this.RotateTo(10, 800, Easing.In); + this.FadeOut(500, Easing.In); } public void UpdateBeatmap(WorkingBeatmap beatmap) @@ -63,23 +66,26 @@ namespace osu.Game.Screens.Select LoadComponentAsync(new BufferedWedgeInfo(beatmap) { Shear = -Shear, - Depth = info?.Depth + 1 ?? 0, + Depth = Info?.Depth + 1 ?? 0, }, newInfo => { - // ensure we ourselves are visible if not already. - if (!IsPresent) - this.FadeIn(250); + State = beatmap == null ? Visibility.Hidden : Visibility.Visible; - info?.FadeOut(250); - info?.Expire(); + Info?.FadeOut(250); + Info?.Expire(); - Add(info = newInfo); + Add(Info = newInfo); }); } public class BufferedWedgeInfo : BufferedContainer { private readonly WorkingBeatmap working; + public OsuSpriteText VersionLabel { get; private set; } + public OsuSpriteText TitleLabel { get; private set; } + public OsuSpriteText ArtistLabel { get; private set; } + public FillFlowContainer MapperContainer { get; private set; } + public FillFlowContainer InfoLabelContainer { get; private set; } public BufferedWedgeInfo(WorkingBeatmap working) { @@ -89,34 +95,8 @@ namespace osu.Game.Screens.Select [BackgroundDependencyLoader] private void load() { - BeatmapInfo beatmapInfo = working.BeatmapInfo; - BeatmapMetadata metadata = beatmapInfo.Metadata ?? working.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); - Beatmap beatmap = working.Beatmap; - - List labels = new List(); - - if (beatmap != null) - { - HitObject lastObject = beatmap.HitObjects.LastOrDefault(); - double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0; - - labels.Add(new InfoLabel(new BeatmapStatistic - { - Name = "Length", - Icon = FontAwesome.fa_clock_o, - Content = beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"), - })); - - labels.Add(new InfoLabel(new BeatmapStatistic - { - Name = "BPM", - Icon = FontAwesome.fa_circle, - Content = getBPMRange(beatmap), - })); - - //get statistics from the current ruleset. - labels.AddRange(beatmapInfo.Ruleset.CreateInstance().GetBeatmapStatistics(working).Select(s => new InfoLabel(s))); - } + var beatmapInfo = working.BeatmapInfo; + var metadata = beatmapInfo.Metadata ?? working.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); PixelSnapping = true; CacheDrawnFrameBuffer = true; @@ -165,7 +145,7 @@ namespace osu.Game.Screens.Select AutoSizeAxes = Axes.Both, Children = new Drawable[] { - new OsuSpriteText + VersionLabel = new OsuSpriteText { Font = @"Exo2.0-MediumItalic", Text = beatmapInfo.Version, @@ -175,69 +155,112 @@ namespace osu.Game.Screens.Select }, new FillFlowContainer { - Name = "Bottom-aligned metadata", - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, + Name = "Centre-aligned metadata", + Anchor = Anchor.CentreLeft, + Origin = Anchor.TopLeft, + Y = -22, Direction = FillDirection.Vertical, Margin = new MarginPadding { Top = 15, Left = 25, Right = 10, Bottom = 20 }, AutoSizeAxes = Axes.Both, Children = new Drawable[] { - new OsuSpriteText + TitleLabel = new OsuSpriteText { Font = @"Exo2.0-MediumItalic", - Text = !string.IsNullOrEmpty(metadata.Source) ? metadata.Source + " — " + metadata.Title : metadata.Title, + Text = string.IsNullOrEmpty(metadata.Source) ? metadata.Title : metadata.Source + " — " + metadata.Title, TextSize = 28, }, - new OsuSpriteText + ArtistLabel = new OsuSpriteText { Font = @"Exo2.0-MediumItalic", Text = metadata.Artist, TextSize = 17, }, - new FillFlowContainer + MapperContainer = new FillFlowContainer { Margin = new MarginPadding { Top = 10 }, Direction = FillDirection.Horizontal, AutoSizeAxes = Axes.Both, - Children = new[] - { - new OsuSpriteText - { - Font = @"Exo2.0-Medium", - Text = "mapped by ", - TextSize = 15, - }, - new OsuSpriteText - { - Font = @"Exo2.0-Bold", - Text = metadata.Author.Username, - TextSize = 15, - }, - } + Children = getMapper(metadata) }, - new FillFlowContainer + InfoLabelContainer = new FillFlowContainer { Margin = new MarginPadding { Top = 20 }, Spacing = new Vector2(20, 0), AutoSizeAxes = Axes.Both, - Children = labels - }, + Children = getInfoLabels() + } } - }, + } }; } + private InfoLabel[] getInfoLabels() + { + var beatmap = working.Beatmap; + var info = working.BeatmapInfo; + + List labels = new List(); + + if (beatmap?.HitObjects?.Count > 0) + { + HitObject lastObject = beatmap.HitObjects.LastOrDefault(); + double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0; + + labels.Add(new InfoLabel(new BeatmapStatistic + { + Name = "Length", + Icon = FontAwesome.fa_clock_o, + Content = beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"), + })); + + labels.Add(new InfoLabel(new BeatmapStatistic + { + Name = "BPM", + Icon = FontAwesome.fa_circle, + Content = getBPMRange(beatmap), + })); + + //get statistics from the current ruleset. + labels.AddRange(info.Ruleset.CreateInstance().GetBeatmapStatistics(working).Select(s => new InfoLabel(s))); + } + + return labels.ToArray(); + } + private string getBPMRange(Beatmap beatmap) { double bpmMax = beatmap.ControlPointInfo.BPMMaximum; double bpmMin = beatmap.ControlPointInfo.BPMMinimum; - if (Precision.AlmostEquals(bpmMin, bpmMax)) return $"{bpmMin:0}"; + if (Precision.AlmostEquals(bpmMin, bpmMax)) + return $"{bpmMin:0}"; return $"{bpmMin:0}-{bpmMax:0} (mostly {beatmap.ControlPointInfo.BPMMode:0})"; } + private OsuSpriteText[] getMapper(BeatmapMetadata metadata) + { + if (string.IsNullOrEmpty(metadata.Author?.Username)) + return Array.Empty(); + + return new[] + { + new OsuSpriteText + { + Font = @"Exo2.0-Medium", + Text = "mapped by ", + TextSize = 15, + }, + new OsuSpriteText + { + Font = @"Exo2.0-Bold", + Text = metadata.Author.Username, + TextSize = 15, + } + }; + } + public class InfoLabel : Container, IHasTooltip { public string TooltipText { get; private set; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 8189624ce1..9e5a2fa633 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Select protected Container LeftContent; - private readonly BeatmapCarousel carousel; + protected readonly BeatmapCarousel Carousel; private readonly BeatmapInfoWedge beatmapInfoWedge; private DialogOverlay dialogOverlay; private BeatmapManager beatmaps; @@ -118,7 +118,7 @@ namespace osu.Game.Screens.Select Width = 0.5f, Children = new Drawable[] { - carousel = new BeatmapCarousel + Carousel = new BeatmapCarousel { Masking = false, RelativeSizeAxes = Axes.Y, @@ -132,7 +132,7 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.X, Height = filter_height, - FilterChanged = c => carousel.Filter(c), + FilterChanged = c => Carousel.Filter(c), Background = { Width = 2 }, Exit = Exit, }, @@ -141,7 +141,6 @@ namespace osu.Game.Screens.Select }, beatmapInfoWedge = new BeatmapInfoWedge { - Alpha = 0, Size = wedged_container_size, RelativeSizeAxes = Axes.X, Margin = new MarginPadding @@ -150,7 +149,7 @@ namespace osu.Game.Screens.Select Right = left_area_padding, }, }, - new ResetScrollContainer(() => carousel.ScrollToSelected()) + new ResetScrollContainer(() => Carousel.ScrollToSelected()) { RelativeSizeAxes = Axes.Y, Width = 250, @@ -210,15 +209,15 @@ namespace osu.Game.Screens.Select initialAddSetsTask = new CancellationTokenSource(); - carousel.BeatmapSets = this.beatmaps.GetAllUsableBeatmapSets(); + Carousel.BeatmapSets = this.beatmaps.GetAllUsableBeatmapSets(); - Beatmap.DisabledChanged += disabled => carousel.AllowSelection = !disabled; + Beatmap.DisabledChanged += disabled => Carousel.AllowSelection = !disabled; Beatmap.TriggerChange(); Beatmap.ValueChanged += b => { if (IsCurrentScreen) - carousel.SelectBeatmap(b?.BeatmapInfo); + Carousel.SelectBeatmap(b?.BeatmapInfo); }; } @@ -232,9 +231,9 @@ namespace osu.Game.Screens.Select { // if we have a pending filter operation, we want to run it now. // it could change selection (ie. if the ruleset has been changed). - carousel.FlushPendingFilterOperations(); + Carousel.FlushPendingFilterOperations(); - carousel.SelectBeatmap(beatmap); + Carousel.SelectBeatmap(beatmap); if (selectionChangedDebounce?.Completed == false) { @@ -302,9 +301,9 @@ namespace osu.Game.Screens.Select private void triggerRandom() { if (GetContainingInputManager().CurrentState.Keyboard.ShiftPressed) - carousel.SelectPreviousRandom(); + Carousel.SelectPreviousRandom(); else - carousel.SelectNextRandom(); + Carousel.SelectNextRandom(); } protected override void OnEntering(Screen last) @@ -419,7 +418,6 @@ namespace osu.Game.Screens.Select backgroundModeBeatmap.FadeTo(1, 250); } - beatmapInfoWedge.State = Visibility.Visible; beatmapInfoWedge.UpdateBeatmap(beatmap); } @@ -437,17 +435,17 @@ namespace osu.Game.Screens.Select } } - private void onBeatmapSetAdded(BeatmapSetInfo s) => carousel.UpdateBeatmapSet(s); - private void onBeatmapSetRemoved(BeatmapSetInfo s) => carousel.RemoveBeatmapSet(s); - private void onBeatmapRestored(BeatmapInfo b) => carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID)); - private void onBeatmapHidden(BeatmapInfo b) => carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID)); + private void onBeatmapSetAdded(BeatmapSetInfo s) => Carousel.UpdateBeatmapSet(s); + private void onBeatmapSetRemoved(BeatmapSetInfo s) => Carousel.RemoveBeatmapSet(s); + private void onBeatmapRestored(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID)); + private void onBeatmapHidden(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID)); private void carouselBeatmapsLoaded() { - if (Beatmap.Value.BeatmapSetInfo?.DeletePending == false) - carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo); - else - carousel.SelectNextRandom(); + if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false) + Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo); + else if (Carousel.SelectedBeatmapSet == null) + Carousel.SelectNextRandom(); } private void delete(BeatmapSetInfo beatmap) diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs index 3e7ab56c99..fbf24eb609 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Tournament { private const string results_filename = "drawings_results.txt"; - public override bool ShowOverlays => false; + public override bool ShowOverlaysOnEnter => false; protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault(); diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index da139775b1..82248c2cb8 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -19,6 +19,6 @@ namespace osu.Game.Tests.Beatmaps protected override Beatmap GetBeatmap() => beatmap; protected override Texture GetBackground() => null; - protected override Track GetTrack() => null; + protected override Track GetTrack() => new TrackVirtual(); } } diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 106f0fa8f3..933781890f 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -22,6 +22,8 @@ namespace osu.Game.Tests.Visual protected Player Player; + private TestWorkingBeatmap working; + /// /// Create a TestCase which runs through the Player screen. /// @@ -75,7 +77,7 @@ namespace osu.Game.Tests.Visual var instance = r.CreateInstance(); - WorkingBeatmap working = new TestWorkingBeatmap(beatmap); + working = new TestWorkingBeatmap(beatmap); working.Mods.Value = new[] { instance.GetAllMods().First(m => m is ModNoFail) }; if (Player != null) @@ -88,10 +90,21 @@ namespace osu.Game.Tests.Visual return player; } + protected override void Update() + { + base.Update(); + + if (working != null) + // note that this will override any mod rate application + working.Track.Rate = Clock.Rate; + } + protected virtual Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) => new Player { InitialBeatmap = beatmap, - AllowPause = false + AllowPause = false, + AllowLeadIn = false, + AllowResults = false, }; private const string test_beatmap_data = diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a5d60f80f6..65c6a3de11 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -143,8 +143,8 @@ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True - - $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll + + $(SolutionDir)\packages\OpenTK.3.0.0-git00021\lib\net20\OpenTK.dll True diff --git a/osu.Game/packages.config b/osu.Game/packages.config index 02ace918de..3ba50388e8 100644 --- a/osu.Game/packages.config +++ b/osu.Game/packages.config @@ -66,7 +66,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - +