From d8de6e289c18f495239c8db4539cb7622733e022 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Aug 2017 17:38:51 +0900 Subject: [PATCH 01/71] Don't start rotating the cursor until it has travelled a minimum disatnce Some people, myself included, were annoyed by the rotation starting too soon (especially when dragging up and left one pixel). --- osu.Game/Graphics/Cursor/MenuCursor.cs | 29 +++++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index 9658cfdb09..ea02feef0e 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Game.Configuration; using System; +using System.Diagnostics; using osu.Framework.Graphics.Textures; namespace osu.Game.Graphics.Cursor @@ -21,20 +22,31 @@ namespace osu.Game.Graphics.Cursor private bool dragging; + private bool startRotation; + protected override bool OnMouseMove(InputState state) { if (dragging) { - Vector2 offset = state.Mouse.Position - state.Mouse.PositionMouseDown ?? state.Mouse.Delta; - float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f; + Debug.Assert(state.Mouse.PositionMouseDown != null); - // Always rotate in the direction of least distance - float diff = (degrees - ActiveCursor.Rotation) % 360; - if (diff < -180) diff += 360; - if (diff > 180) diff -= 360; - degrees = ActiveCursor.Rotation + diff; + // don't start rotating until we're moved a minimum distance away from the mouse down location, + // else it can have an annoying effect. + startRotation |= Vector2.Distance(state.Mouse.Position, state.Mouse.PositionMouseDown.Value) > 30; - ActiveCursor.RotateTo(degrees, 600, Easing.OutQuint); + if (startRotation) + { + Vector2 offset = state.Mouse.Position - state.Mouse.PositionMouseDown.Value; + float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f; + + // Always rotate in the direction of least distance + float diff = (degrees - ActiveCursor.Rotation) % 360; + if (diff < -180) diff += 360; + if (diff > 180) diff -= 360; + degrees = ActiveCursor.Rotation + diff; + + ActiveCursor.RotateTo(degrees, 600, Easing.OutQuint); + } } return base.OnMouseMove(state); @@ -61,6 +73,7 @@ namespace osu.Game.Graphics.Cursor if (!state.Mouse.HasMainButtonPressed) { dragging = false; + startRotation = false; ((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, Easing.OutQuint); ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf); From b46a51fd8b7ccdbdd6019cff246a82c2efa72444 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 22 Aug 2017 18:36:32 +0900 Subject: [PATCH 02/71] Make the speed adjustment containers ordered decreasingly by their control point start time. --- osu.Game/Rulesets/UI/ScrollingPlayfield.cs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs index 524665487d..6f0b7ac4ac 100644 --- a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs @@ -151,7 +151,7 @@ namespace osu.Game.Rulesets.UI /// public readonly BindableBool Reversed = new BindableBool(); - private readonly Container speedAdjustments; + private readonly SortedContainer speedAdjustments; public IReadOnlyList SpeedAdjustments => speedAdjustments; private readonly SpeedAdjustmentContainer defaultSpeedAdjustment; @@ -166,7 +166,7 @@ namespace osu.Game.Rulesets.UI { this.scrollingAxes = scrollingAxes; - AddInternal(speedAdjustments = new Container { RelativeSizeAxes = Axes.Both }); + AddInternal(speedAdjustments = new SortedContainer { RelativeSizeAxes = Axes.Both }); // Default speed adjustment AddSpeedAdjustment(defaultSpeedAdjustment = new SpeedAdjustmentContainer(new MultiplierControlPoint(0))); @@ -257,7 +257,21 @@ namespace osu.Game.Rulesets.UI /// /// The time to find the active at. /// The active at . Null if there are no speed adjustments. - private SpeedAdjustmentContainer adjustmentContainerAt(double time) => speedAdjustments.LastOrDefault(c => c.CanContain(time)) ?? defaultSpeedAdjustment; + private SpeedAdjustmentContainer adjustmentContainerAt(double time) => speedAdjustments.First(c => c.CanContain(time)) ?? defaultSpeedAdjustment; + + private class SortedContainer : Container + { + protected override int Compare(Drawable x, Drawable y) + { + var sX = (SpeedAdjustmentContainer)x; + var sY = (SpeedAdjustmentContainer)y; + + int result = sY.ControlPoint.StartTime.CompareTo(sX.ControlPoint.StartTime); + if (result != 0) + return result; + return base.Compare(x, y); + } + } } } } From 137964b79297e27b9eb36a61e176f2b929068094 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 22 Aug 2017 18:36:56 +0900 Subject: [PATCH 03/71] Fix hit objects not getting added to the correct speed adjustment container. --- .../Timing/SpeedAdjustmentContainer.cs | 5 --- osu.Game/Rulesets/UI/ScrollingPlayfield.cs | 38 +++++++------------ 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs index 3b13fdf00a..df0abd28df 100644 --- a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs +++ b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs @@ -98,11 +98,6 @@ namespace osu.Game.Rulesets.Timing base.Add(drawable); } - /// - /// Whether a falls within this s affecting timespan. - /// - public bool CanContain(DrawableHitObject hitObject) => CanContain(hitObject.HitObject.StartTime); - /// /// Whether a point in time falls within this s affecting timespan. /// diff --git a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs index 6f0b7ac4ac..550740c5ec 100644 --- a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs @@ -181,26 +181,25 @@ namespace osu.Game.Rulesets.UI speedAdjustment.ScrollingAxes = scrollingAxes; speedAdjustment.VisibleTimeRange.BindTo(VisibleTimeRange); speedAdjustment.Reversed.BindTo(Reversed); - speedAdjustments.Add(speedAdjustment); - // We now need to re-sort the hit objects in the last speed adjustment prior to this one, to see if they need a new parent - var previousSpeedAdjustment = speedAdjustments.LastOrDefault(s => s != speedAdjustment && s.ControlPoint.StartTime <= speedAdjustment.ControlPoint.StartTime); - if (previousSpeedAdjustment == null) - return; - - for (int i = 0; i < previousSpeedAdjustment.Children.Count; i++) + if (speedAdjustments.Count > 0) { - DrawableHitObject hitObject = previousSpeedAdjustment[i]; + var existingAdjustment = adjustmentContainerAt(speedAdjustment.ControlPoint.StartTime); + for (int i = 0; i < existingAdjustment.Count; i++) + { + DrawableHitObject hitObject = existingAdjustment[i]; - var newSpeedAdjustment = adjustmentContainerFor(hitObject); - if (newSpeedAdjustment == previousSpeedAdjustment) - continue; + if (!speedAdjustment.CanContain(hitObject.HitObject.StartTime)) + continue; - previousSpeedAdjustment.Remove(hitObject); - newSpeedAdjustment.Add(hitObject); + existingAdjustment.Remove(hitObject); + speedAdjustment.Add(hitObject); - i--; + i--; + } } + + speedAdjustments.Add(speedAdjustment); } /// @@ -237,20 +236,11 @@ namespace osu.Game.Rulesets.UI if (!(hitObject is IScrollingHitObject)) throw new InvalidOperationException($"Hit objects added to a {nameof(ScrollingHitObjectContainer)} must implement {nameof(IScrollingHitObject)}."); - adjustmentContainerFor(hitObject).Add(hitObject); + adjustmentContainerAt(hitObject.HitObject.StartTime).Add(hitObject); } public override bool Remove(DrawableHitObject hitObject) => speedAdjustments.Any(s => s.Remove(hitObject)); - /// - /// Finds the which provides the speed adjustment active at the start time - /// of a hit object. If there is no active at the start time of the hit object, - /// then the first (time-wise) speed adjustment is returned. - /// - /// The hit object to find the active for. - /// The active at 's start time. Null if there are no speed adjustments. - private SpeedAdjustmentContainer adjustmentContainerFor(DrawableHitObject hitObject) => speedAdjustments.LastOrDefault(c => c.CanContain(hitObject)) ?? defaultSpeedAdjustment; - /// /// Finds the which provides the speed adjustment active at a time. /// If there is no active at the time, then the first (time-wise) speed adjustment is returned. From e5d985838f55d95742003d829b6759904786a18d Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 22 Aug 2017 18:37:49 +0900 Subject: [PATCH 04/71] Set ScrollingAxes and RelativeChildOffset a bit more safely. --- .../Rulesets/Timing/LinearScrollingContainer.cs | 8 +++----- osu.Game/Rulesets/Timing/ScrollingContainer.cs | 2 ++ .../Rulesets/Timing/SpeedAdjustmentContainer.cs | 13 +++++++------ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/osu.Game/Rulesets/Timing/LinearScrollingContainer.cs b/osu.Game/Rulesets/Timing/LinearScrollingContainer.cs index db497b7664..f8e87bc022 100644 --- a/osu.Game/Rulesets/Timing/LinearScrollingContainer.cs +++ b/osu.Game/Rulesets/Timing/LinearScrollingContainer.cs @@ -10,12 +10,10 @@ namespace osu.Game.Rulesets.Timing /// internal class LinearScrollingContainer : ScrollingContainer { - private readonly Axes scrollingAxes; private readonly MultiplierControlPoint controlPoint; - public LinearScrollingContainer(Axes scrollingAxes, MultiplierControlPoint controlPoint) + public LinearScrollingContainer(MultiplierControlPoint controlPoint) { - this.scrollingAxes = scrollingAxes; this.controlPoint = controlPoint; } @@ -23,8 +21,8 @@ namespace osu.Game.Rulesets.Timing { base.Update(); - if ((scrollingAxes & Axes.X) > 0) X = (float)(controlPoint.StartTime - Time.Current); - if ((scrollingAxes & Axes.Y) > 0) Y = (float)(controlPoint.StartTime - Time.Current); + if ((ScrollingAxes & Axes.X) > 0) X = (float)(controlPoint.StartTime - Time.Current); + if ((ScrollingAxes & Axes.Y) > 0) Y = (float)(controlPoint.StartTime - Time.Current); } } } diff --git a/osu.Game/Rulesets/Timing/ScrollingContainer.cs b/osu.Game/Rulesets/Timing/ScrollingContainer.cs index 9bb32ead77..ad5fc7c2f0 100644 --- a/osu.Game/Rulesets/Timing/ScrollingContainer.cs +++ b/osu.Game/Rulesets/Timing/ScrollingContainer.cs @@ -96,6 +96,8 @@ namespace osu.Game.Rulesets.Timing { base.Update(); + RelativeChildOffset = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)ControlPoint.StartTime : 0, (ScrollingAxes & Axes.Y) > 0 ? (float)ControlPoint.StartTime : 0); + // We want our size and position-space along the scrolling axes to span our duration to completely enclose all the hit objects Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)Duration : Size.X, (ScrollingAxes & Axes.Y) > 0 ? (float)Duration : Size.Y); // And we need to make sure the hit object's position-space doesn't change due to our resizing diff --git a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs index df0abd28df..d3bd7685da 100644 --- a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs +++ b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs @@ -31,7 +31,11 @@ namespace osu.Game.Rulesets.Timing /// /// The axes which the content of this container will scroll through. /// - public Axes ScrollingAxes { get; internal set; } + public Axes ScrollingAxes + { + get { return scrollingContainer.ScrollingAxes; } + set { scrollingContainer.ScrollingAxes = value; } + } public override bool RemoveWhenNotAlive => false; @@ -52,11 +56,8 @@ namespace osu.Game.Rulesets.Timing RelativeSizeAxes = Axes.Both; scrollingContainer = CreateScrollingContainer(); - - scrollingContainer.ScrollingAxes = ScrollingAxes; scrollingContainer.ControlPoint = ControlPoint; scrollingContainer.VisibleTimeRange.BindTo(VisibleTimeRange); - scrollingContainer.RelativeChildOffset = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)ControlPoint.StartTime : 0, (ScrollingAxes & Axes.Y) > 0 ? (float)ControlPoint.StartTime : 0); AddInternal(content = scrollingContainer); } @@ -107,6 +108,6 @@ namespace osu.Game.Rulesets.Timing /// Creates the which contains the scrolling s of this container. /// /// The . - protected virtual ScrollingContainer CreateScrollingContainer() => new LinearScrollingContainer(ScrollingAxes, ControlPoint); + protected virtual ScrollingContainer CreateScrollingContainer() => new LinearScrollingContainer(ControlPoint); } -} \ No newline at end of file +} From 1964bc72e5c933c051b84b483bf62d0c223e0277 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 22 Aug 2017 18:39:09 +0900 Subject: [PATCH 05/71] Should be FirstOrDefault for hit objects occuriung before the first control point. --- osu.Game/Rulesets/UI/ScrollingPlayfield.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs index 550740c5ec..007f2af803 100644 --- a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs @@ -247,7 +247,7 @@ namespace osu.Game.Rulesets.UI /// /// The time to find the active at. /// The active at . Null if there are no speed adjustments. - private SpeedAdjustmentContainer adjustmentContainerAt(double time) => speedAdjustments.First(c => c.CanContain(time)) ?? defaultSpeedAdjustment; + private SpeedAdjustmentContainer adjustmentContainerAt(double time) => speedAdjustments.FirstOrDefault(c => c.CanContain(time)) ?? defaultSpeedAdjustment; private class SortedContainer : Container { From ad63cbf45505c8fe2ba027cf3d9fe78e551e5a56 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 22 Aug 2017 18:54:41 +0900 Subject: [PATCH 06/71] Fix default comparator being inverted. --- osu.Game/Rulesets/UI/ScrollingPlayfield.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs index 007f2af803..7143401824 100644 --- a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs @@ -259,7 +259,7 @@ namespace osu.Game.Rulesets.UI int result = sY.ControlPoint.StartTime.CompareTo(sX.ControlPoint.StartTime); if (result != 0) return result; - return base.Compare(x, y); + return base.Compare(y, x); } } } From d64071f3de78291766b494d1078099f051badd3e Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 22 Aug 2017 18:55:27 +0900 Subject: [PATCH 07/71] Fix up + make test case more sane. --- .../Visual/TestCaseScrollingPlayfield.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs index 2d49b2d0f9..c8c95da618 100644 --- a/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs +++ b/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs @@ -115,18 +115,18 @@ namespace osu.Desktop.Tests.Visual Assert.AreEqual(1, speedAdjustments[3].ControlPoint.Multiplier); // Check insertion of hit objects - Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[0])); - Assert.IsTrue(speedAdjustments[0].Contains(hitObjects[1])); - Assert.IsTrue(speedAdjustments[1].Contains(hitObjects[2])); - Assert.IsTrue(speedAdjustments[2].Contains(hitObjects[3])); - Assert.IsTrue(speedAdjustments[3].Contains(hitObjects[4])); - Assert.IsTrue(speedAdjustments[3].Contains(hitObjects[5])); + Assert.IsTrue(hitObjectContainer.SpeedAdjustments[4].Contains(hitObjects[0])); + Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1])); + Assert.IsTrue(hitObjectContainer.SpeedAdjustments[2].Contains(hitObjects[2])); + Assert.IsTrue(hitObjectContainer.SpeedAdjustments[1].Contains(hitObjects[3])); + Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[4])); + Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[5])); - hitObjectContainer.RemoveSpeedAdjustment(speedAdjustments[1]); + hitObjectContainer.RemoveSpeedAdjustment(hitObjectContainer.SpeedAdjustments[3]); - // The hit object contained in this speed adjustment should be resorted into the previous one + // The hit object contained in this speed adjustment should be resorted into the one occuring before it - Assert.IsTrue(speedAdjustments[0].Contains(hitObjects[2])); + Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1])); } private class TestRulesetContainer : ScrollingRulesetContainer From 322dfe0250eef71d0321e53bd70d4238d1ad48a4 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 22 Aug 2017 19:17:12 +0900 Subject: [PATCH 08/71] Fix ScrollingContainer possibly not getting the correct size to cover hit objects. --- osu.Game/Rulesets/Timing/ScrollingContainer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Timing/ScrollingContainer.cs b/osu.Game/Rulesets/Timing/ScrollingContainer.cs index ad5fc7c2f0..e4b098d2bd 100644 --- a/osu.Game/Rulesets/Timing/ScrollingContainer.cs +++ b/osu.Game/Rulesets/Timing/ScrollingContainer.cs @@ -74,8 +74,7 @@ namespace osu.Game.Rulesets.Timing // absolutely-sized element along the scrolling axes and adding a corresponding duration value. This introduces a bit of error, but will never under-estimate.ion. // Find the largest element that is absolutely-sized along ScrollingAxes - float maxAbsoluteSize = Children.Where(c => (c.RelativeSizeAxes & ScrollingAxes) == 0) - .Select(c => (ScrollingAxes & Axes.X) > 0 ? c.Width : c.Height) + float maxAbsoluteSize = Children.Select(c => (ScrollingAxes & Axes.X) > 0 ? c.DrawWidth : c.DrawHeight) .DefaultIfEmpty().Max(); float ourAbsoluteSize = (ScrollingAxes & Axes.X) > 0 ? DrawWidth : DrawHeight; From ab0e3ccc5502074cb493cfbb44cf160443467745 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Aug 2017 19:33:18 +0900 Subject: [PATCH 09/71] Make SimpleNotifications support word wrap --- osu.Game/Overlays/Notifications/SimpleNotification.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Notifications/SimpleNotification.cs b/osu.Game/Overlays/Notifications/SimpleNotification.cs index e10cc26546..daf1ac838d 100644 --- a/osu.Game/Overlays/Notifications/SimpleNotification.cs +++ b/osu.Game/Overlays/Notifications/SimpleNotification.cs @@ -4,17 +4,16 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using OpenTK; namespace osu.Game.Overlays.Notifications { public class SimpleNotification : Notification { - private string text; + private string text = string.Empty; public string Text { get { return text; } @@ -36,7 +35,7 @@ namespace osu.Game.Overlays.Notifications } } - private readonly SpriteText textDrawable; + private readonly TextFlowContainer textDrawable; private readonly SpriteIcon iconDrawable; protected Box IconBackgound; @@ -59,9 +58,8 @@ namespace osu.Game.Overlays.Notifications } }); - Content.Add(textDrawable = new OsuSpriteText + Content.Add(textDrawable = new TextFlowContainer(t => t.TextSize = 16) { - TextSize = 16, Colour = OsuColour.Gray(128), AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, From 0ea6c65be6525c295902f3cd884c97d82bedb1d9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Aug 2017 19:34:06 +0900 Subject: [PATCH 10/71] Fix potentially racey behaviour of versionManager initialisation Changing the state while the container wasn't necessarily completed loading caused inconsistent behaviour. --- osu.Desktop/OsuGameDesktop.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 88c8a206c8..a623347540 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -20,16 +20,11 @@ namespace osu.Desktop { internal class OsuGameDesktop : OsuGame { - private readonly VersionManager versionManager; + private VersionManager versionManager; public OsuGameDesktop(string[] args = null) : base(args) { - versionManager = new VersionManager - { - Depth = int.MinValue, - State = Visibility.Hidden - }; } public override Storage GetStorageForStableInstall() @@ -88,11 +83,15 @@ namespace osu.Desktop { base.LoadComplete(); - LoadComponentAsync(versionManager, Add); + LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }); + ScreenChanged += s => { - if (!versionManager.IsPresent && s is Intro) + if (s is Intro && s.ChildScreen == null) + { + Add(versionManager); versionManager.State = Visibility.Visible; + } }; } From 9f69aa908225ea55dbdc9ee172c1688183d721bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Aug 2017 19:41:39 +0900 Subject: [PATCH 11/71] Show a notification after a successful update Allows access to the github changelog --- osu.Desktop/Overlays/VersionManager.cs | 48 +++++++++++++++++++++- osu.Game/Configuration/OsuConfigManager.cs | 5 ++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index b445340f50..dab2d160c5 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -19,6 +20,7 @@ using OpenTK.Graphics; using System.Net.Http; using osu.Framework.Logging; using osu.Game; +using osu.Game.Configuration; namespace osu.Desktop.Overlays { @@ -26,18 +28,24 @@ namespace osu.Desktop.Overlays { private UpdateManager updateManager; private NotificationOverlay notificationOverlay; + private OsuConfigManager config; + private OsuGameBase game; public override bool HandleInput => false; [BackgroundDependencyLoader] - private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game) + private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config) { notificationOverlay = notification; + this.config = config; + this.game = game; AutoSizeAxes = Axes.Both; Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; + Alpha = 0; + State = Visibility.Hidden; Children = new Drawable[] { @@ -91,6 +99,44 @@ namespace osu.Desktop.Overlays checkForUpdateAsync(); } + protected override void LoadComplete() + { + base.LoadComplete(); + + State = Visibility.Visible; + + var version = game.Version; + var lastVersion = config.Get(OsuSetting.Version); + if (char.IsNumber(version[0]) && version != lastVersion) + { + config.Set(OsuSetting.Version, version); + + // 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); + } + } + + private class UpdateCompleteNotification : SimpleNotification + { + public UpdateCompleteNotification(string version) + { + Text = $"You are now running osu!lazer {version}.\nClick to see what's new!"; + Icon = FontAwesome.fa_check_square; + Activated = delegate + { + Process.Start($"https://github.com/ppy/osu/releases/tag/v{version}"); + return true; + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + IconBackgound.Colour = colours.BlueDark; + } + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 6b07d5c967..44a6af841c 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -70,6 +70,8 @@ namespace osu.Game.Configuration // Update Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); + + Set(OsuSetting.Version, string.Empty); } public OsuConfigManager(Storage storage) : base(storage) @@ -106,6 +108,7 @@ namespace osu.Game.Configuration SnakingInSliders, SnakingOutSliders, ShowFpsDisplay, - ChatDisplayHeight + ChatDisplayHeight, + Version } } From 480d839d678e4eb1a537a559984c8718f3a09c49 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Aug 2017 19:51:42 +0900 Subject: [PATCH 12/71] Allow the notification overlay to close when all notifications are dismissed --- osu.Game/Overlays/NotificationOverlay.cs | 11 ++++++++++- osu.Game/Overlays/Notifications/Notification.cs | 4 ++-- .../Overlays/Notifications/NotificationSection.cs | 2 ++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 7eabb592c6..5b9c3d20a0 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays private ScrollContainer scrollContainer; private FlowContainer sections; - [BackgroundDependencyLoader(permitNulls: true)] + [BackgroundDependencyLoader] private void load() { Width = width; @@ -72,6 +72,13 @@ namespace osu.Game.Overlays private int runningDepth; + private void notificationClosed() + { + // hide ourselves if all notifications have been dismissed. + if (sections.Select(c => c.DisplayedCount).Sum() > 0) + State = Visibility.Hidden; + } + public void Post(Notification notification) { Schedule(() => @@ -81,6 +88,8 @@ namespace osu.Game.Overlays ++runningDepth; notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth; + notification.Closed += notificationClosed; + var hasCompletionTarget = notification as IHasCompletionTarget; if (hasCompletionTarget != null) hasCompletionTarget.CompletionTarget = Post; diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 14446a468c..b63efd3226 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -19,9 +19,9 @@ namespace osu.Game.Overlays.Notifications public abstract class Notification : Container { /// - /// Use requested close. + /// User requested close. /// - public Action Closed; + public event Action Closed; /// /// Run on user activating the notification. Return true to close. diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index efd3b39ee2..705800d4aa 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -24,6 +24,8 @@ namespace osu.Game.Overlays.Notifications private FlowContainer notifications; + public int DisplayedCount => notifications.Count; + public void Add(Notification notification) { notifications.Add(notification); From db275517096197d86e721172d274d6b2d2356157 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 22 Aug 2017 20:51:20 +0900 Subject: [PATCH 13/71] Make hit objects put in ScrollingContainers strictly ordered by start time This won't change anything as is since all hit objects are given a depth at the moment. --- osu.Game/Rulesets/Timing/ScrollingContainer.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/osu.Game/Rulesets/Timing/ScrollingContainer.cs b/osu.Game/Rulesets/Timing/ScrollingContainer.cs index e4b098d2bd..d2a6939dd3 100644 --- a/osu.Game/Rulesets/Timing/ScrollingContainer.cs +++ b/osu.Game/Rulesets/Timing/ScrollingContainer.cs @@ -45,18 +45,15 @@ namespace osu.Game.Rulesets.Timing RelativePositionAxes = Axes.Both; } - public override void InvalidateFromChild(Invalidation invalidation) + protected override int Compare(Drawable x, Drawable y) { - // We only want to re-compute our size when a child's size or position has changed - if ((invalidation & Invalidation.RequiredParentSizeToFit) == 0) - { - base.InvalidateFromChild(invalidation); - return; - } + var hX = (DrawableHitObject)x; + var hY = (DrawableHitObject)y; - durationBacking.Invalidate(); - - base.InvalidateFromChild(invalidation); + int result = hY.HitObject.StartTime.CompareTo(hX.HitObject.StartTime); + if (result != 0) + return result; + return base.Compare(y, x); } private double computeDuration() From d080a7e970252059cef1df35f5f3080d77cbecfc Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 22 Aug 2017 20:51:53 +0900 Subject: [PATCH 14/71] Determine size of ScrollingContainer a bit better to avoid taiko weirdness --- .../Objects/Drawables/DrawableHit.cs | 15 ++++--- .../Objects/Drawables/DrawableSwell.cs | 2 + .../Drawables/DrawableTaikoHitObject.cs | 4 +- .../Rulesets/Timing/ScrollingContainer.cs | 41 +++++++------------ 4 files changed, 27 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 5fcae7d0f4..6b041dfe25 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -28,14 +28,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables FillMode = FillMode.Fit; } - protected override void LoadComplete() - { - base.LoadComplete(); - - // We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize - Width *= Parent.RelativeChildSize.X; - } - protected override void CheckJudgement(bool userTriggered) { if (!userTriggered) @@ -71,6 +63,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return UpdateJudgement(true); } + protected override void Update() + { + base.Update(); + + Size = BaseSize * Parent.RelativeChildSize; + } + protected override void UpdateState(ArmedState state) { var circlePiece = MainPiece as CirclePiece; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index fc3bc82520..4eb5c46838 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -199,6 +199,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { base.Update(); + Size = BaseSize * Parent.RelativeChildSize; + // Make the swell stop at the hit target X = (float)Math.Max(Time.Current, HitObject.StartTime); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 765002fb34..46cdfcc365 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public override Vector2 OriginPosition => new Vector2(DrawHeight / 2); + protected readonly Vector2 BaseSize; + protected readonly TaikoPiece MainPiece; public new TaikoHitType HitObject; @@ -29,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Origin = Anchor.Custom; RelativeSizeAxes = Axes.Both; - Size = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE); + Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE); Add(MainPiece = CreateMainPiece()); MainPiece.KiaiMode = HitObject.Kiai; diff --git a/osu.Game/Rulesets/Timing/ScrollingContainer.cs b/osu.Game/Rulesets/Timing/ScrollingContainer.cs index d2a6939dd3..c8da8d7ac9 100644 --- a/osu.Game/Rulesets/Timing/ScrollingContainer.cs +++ b/osu.Game/Rulesets/Timing/ScrollingContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Linq; using osu.Framework.Caching; using osu.Framework.Configuration; @@ -34,7 +35,7 @@ namespace osu.Game.Rulesets.Timing /// internal MultiplierControlPoint ControlPoint; - private Cached durationBacking; + private Cached durationBacking = new Cached(); /// /// Creates a new . @@ -56,35 +57,23 @@ namespace osu.Game.Rulesets.Timing return base.Compare(y, x); } - private double computeDuration() + public override void Add(DrawableHitObject drawable) { - if (!Children.Any()) - return 0; - - double baseDuration = Children.Max(c => (c.HitObject as IHasEndTime)?.EndTime ?? c.HitObject.StartTime) - ControlPoint.StartTime; - - // If we have a singular hit object at the timing section's start time, let's set a sane default duration - if (baseDuration == 0) - baseDuration = 1; - - // This container needs to resize such that it completely encloses the hit objects to avoid masking optimisations. This is done by converting the largest - // absolutely-sized element along the scrolling axes and adding a corresponding duration value. This introduces a bit of error, but will never under-estimate.ion. - - // Find the largest element that is absolutely-sized along ScrollingAxes - float maxAbsoluteSize = Children.Select(c => (ScrollingAxes & Axes.X) > 0 ? c.DrawWidth : c.DrawHeight) - .DefaultIfEmpty().Max(); - - float ourAbsoluteSize = (ScrollingAxes & Axes.X) > 0 ? DrawWidth : DrawHeight; - - // Add the extra duration to account for the absolute size - baseDuration *= 1 + maxAbsoluteSize / ourAbsoluteSize; - - return baseDuration; + durationBacking.Invalidate(); + base.Add(drawable); } + public override bool Remove(DrawableHitObject drawable) + { + durationBacking.Invalidate(); + return base.Remove(drawable); + } + + // Todo: This may underestimate the size of the hit object in some cases, but won't be too much of a problem for now + private double computeDuration() => Math.Max(0, Children.Select(c => (c.HitObject as IHasEndTime)?.EndTime ?? c.HitObject.StartTime).DefaultIfEmpty().Max() - ControlPoint.StartTime) + 1000; + /// - /// The maximum duration of any one hit object inside this . This is calculated as the maximum - /// duration of all hit objects relative to this 's . + /// An approximate total duration of this scrolling container. /// public double Duration => durationBacking.IsValid ? durationBacking : (durationBacking.Value = computeDuration()); From f8576d44b12feb101f6983a18784828b9f16364f Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 22 Aug 2017 21:08:27 +0900 Subject: [PATCH 15/71] Add some more xmldoc. --- osu.Game/Rulesets/UI/ScrollingPlayfield.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs index 7143401824..92845c3f3a 100644 --- a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs @@ -173,7 +173,8 @@ namespace osu.Game.Rulesets.UI } /// - /// Adds a to this container. + /// Adds a to this container, re-sorting all hit objects + /// in the last that occurred (time-wise) before it. /// /// The . public void AddSpeedAdjustment(SpeedAdjustmentContainer speedAdjustment) @@ -184,6 +185,8 @@ namespace osu.Game.Rulesets.UI if (speedAdjustments.Count > 0) { + // We need to re-sort all hit objects in the speed adjustment container prior to figure out if they + // should now lie within this one var existingAdjustment = adjustmentContainerAt(speedAdjustment.ControlPoint.StartTime); for (int i = 0; i < existingAdjustment.Count; i++) { From 71af30d2225e4c74bd2210ef37694e7ec8a15bd0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Aug 2017 22:48:50 +0900 Subject: [PATCH 16/71] Remove redundant visibility changes --- osu.Desktop/Overlays/VersionManager.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index dab2d160c5..1c0c1475b1 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -45,7 +45,6 @@ namespace osu.Desktop.Overlays Origin = Anchor.BottomCentre; Alpha = 0; - State = Visibility.Hidden; Children = new Drawable[] { @@ -103,11 +102,9 @@ namespace osu.Desktop.Overlays { base.LoadComplete(); - State = Visibility.Visible; - var version = game.Version; var lastVersion = config.Get(OsuSetting.Version); - if (char.IsNumber(version[0]) && version != lastVersion) + if (game.IsDeployedBuild && version != lastVersion) { config.Set(OsuSetting.Version, version); From ef2d9ffede813a5c2ddff6d01af1f269ea3a9496 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Aug 2017 23:43:10 +0900 Subject: [PATCH 17/71] Fix redundant initialiser --- osu.Game/Rulesets/Timing/ScrollingContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Timing/ScrollingContainer.cs b/osu.Game/Rulesets/Timing/ScrollingContainer.cs index c8da8d7ac9..6e77c49e3d 100644 --- a/osu.Game/Rulesets/Timing/ScrollingContainer.cs +++ b/osu.Game/Rulesets/Timing/ScrollingContainer.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Timing /// internal MultiplierControlPoint ControlPoint; - private Cached durationBacking = new Cached(); + private Cached durationBacking; /// /// Creates a new . From dbde27ceefdeae85ad2927ba59baae21b5f62c81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Aug 2017 23:43:40 +0900 Subject: [PATCH 18/71] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index ba70b8eaa9..74f644bad0 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit ba70b8eaa9b79d4248873d4399f3b9e918fc3c8f +Subproject commit 74f644bad039606e242d8782014d8c249a38b6a3 From fd3239ad15fa0833a3b6f2c4b7010840e60f6300 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 12:36:53 +0900 Subject: [PATCH 19/71] Fix KeyBindingStore not getting correct defaults for variants. --- osu.Game/Input/KeyBindingStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 56302acafe..c5ba1683dd 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -22,7 +22,7 @@ namespace osu.Game.Input { var ruleset = info.CreateInstance(); foreach (var variant in ruleset.AvailableVariants) - insertDefaults(ruleset.GetDefaultKeyBindings(), info.ID, variant); + insertDefaults(ruleset.GetDefaultKeyBindings(variant), info.ID, variant); } } From a6266850d60c6366666a4e0223f6e01d0183fa8a Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 12:46:28 +0900 Subject: [PATCH 20/71] Make KeyBindingRow take a non-action enum. --- osu-framework | 2 +- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu-framework b/osu-framework index 74f644bad0..1fd9259e86 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 74f644bad039606e242d8782014d8c249a38b6a3 +Subproject commit 1fd9259e86379838dbbc43c12a2238df83800fc0 diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 6c697c8660..c182382d70 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -1,7 +1,6 @@ // 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.Allocation; @@ -22,7 +21,7 @@ namespace osu.Game.Overlays.KeyBinding { internal class KeyBindingRow : Container, IFilterable { - private readonly Enum action; + private readonly object action; private readonly IEnumerable bindings; private const float transition_time = 150; @@ -50,7 +49,7 @@ namespace osu.Game.Overlays.KeyBinding public string[] FilterTerms => new[] { text.Text }.Concat(bindings.Select(b => b.KeyCombination.ReadableString())).ToArray(); - public KeyBindingRow(Enum action, IEnumerable bindings) + public KeyBindingRow(object action, IEnumerable bindings) { this.action = action; this.bindings = bindings; From b2db550cb457abf41bcf12658731d6f9768655d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Aug 2017 12:47:20 +0900 Subject: [PATCH 21/71] Fix notification count including fading (already closed) notifications --- osu.Game/Overlays/NotificationOverlay.cs | 2 +- osu.Game/Overlays/Notifications/Notification.cs | 6 +++--- osu.Game/Overlays/Notifications/NotificationSection.cs | 7 ++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 5b9c3d20a0..260214a14f 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -75,7 +75,7 @@ namespace osu.Game.Overlays private void notificationClosed() { // hide ourselves if all notifications have been dismissed. - if (sections.Select(c => c.DisplayedCount).Sum() > 0) + if (sections.Select(c => c.DisplayedCount).Sum() == 0) State = Visibility.Hidden; } diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index b63efd3226..422051364e 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -142,12 +142,12 @@ namespace osu.Game.Overlays.Notifications NotificationContent.MoveToX(0, 500, Easing.OutQuint); } - private bool wasClosed; + public bool WasClosed; public virtual void Close() { - if (wasClosed) return; - wasClosed = true; + if (WasClosed) return; + WasClosed = true; Closed?.Invoke(); this.FadeOut(100); diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index 705800d4aa..09768ba0ea 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -24,12 +24,9 @@ namespace osu.Game.Overlays.Notifications private FlowContainer notifications; - public int DisplayedCount => notifications.Count; + public int DisplayedCount => notifications.Count(n => !n.WasClosed); - public void Add(Notification notification) - { - notifications.Add(notification); - } + public void Add(Notification notification) => notifications.Add(notification); public IEnumerable AcceptTypes; From be96fb32b30aeb4c2b461c0974a34c8c03545967 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 12:47:47 +0900 Subject: [PATCH 22/71] Make RulesetContainer load KeyBindingInputManager in load() --- osu.Game/Rulesets/UI/RulesetContainer.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 2f1cb915f3..ebf570fecc 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.UI /// /// The key conversion input manager for this RulesetContainer. /// - public readonly PassThroughInputManager KeyBindingInputManager; + public PassThroughInputManager KeyBindingInputManager; /// /// Whether we are currently providing the local user a gameplay cursor. @@ -76,6 +76,11 @@ namespace osu.Game.Rulesets.UI internal RulesetContainer(Ruleset ruleset) { Ruleset = ruleset; + } + + [BackgroundDependencyLoader] + private void load() + { KeyBindingInputManager = CreateInputManager(); KeyBindingInputManager.RelativeSizeAxes = Axes.Both; } From 38a4c841636144d2d46405166d0cf7ee0e62a44f Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 12:48:53 +0900 Subject: [PATCH 23/71] Load SettingsSubsection in load() Fixes header not being displayed with variant bindings. This follows what SettingsSection does. --- .../Overlays/Settings/SettingsSubsection.cs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSubsection.cs b/osu.Game/Overlays/Settings/SettingsSubsection.cs index ac6d2fa239..0fbb5b92f7 100644 --- a/osu.Game/Overlays/Settings/SettingsSubsection.cs +++ b/osu.Game/Overlays/Settings/SettingsSubsection.cs @@ -7,14 +7,15 @@ using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; namespace osu.Game.Overlays.Settings { public abstract class SettingsSubsection : FillFlowContainer, IHasFilterableChildren { - protected override Container Content => content; + protected override Container Content => FlowContent; - private readonly Container content; + protected readonly FillFlowContainer FlowContent; protected abstract string Header { get; } @@ -33,6 +34,19 @@ namespace osu.Game.Overlays.Settings RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; + + FlowContent = new FillFlowContainer + { + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }; + } + + [BackgroundDependencyLoader] + private void load() + { AddRangeInternal(new Drawable[] { new OsuSpriteText @@ -41,13 +55,7 @@ namespace osu.Game.Overlays.Settings Margin = new MarginPadding { Bottom = 10 }, Font = @"Exo2.0-Black", }, - content = new FillFlowContainer - { - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5), - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, + FlowContent }); } } From c1860f2ce21450801151fea1bc408890d2fcf566 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 12:49:30 +0900 Subject: [PATCH 24/71] Make KeyBindingOverlay support variants as settings sub sections. --- .../KeyBinding/GlobalKeyBindingsSection.cs | 24 ++++++++++++------ ...ngsSection.cs => KeyBindingsSubsection.cs} | 25 ++++++++----------- .../KeyBinding/RulesetBindingsSection.cs | 16 ++++++++---- .../KeyBinding/VariantBindingsSubsection.cs | 23 +++++++++++++++++ osu.Game/Overlays/KeyBindingOverlay.cs | 2 +- osu.Game/osu.Game.csproj | 3 ++- 6 files changed, 63 insertions(+), 30 deletions(-) rename osu.Game/Overlays/KeyBinding/{KeyBindingsSection.cs => KeyBindingsSubsection.cs} (57%) create mode 100644 osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs index 7dd9919e5d..77de9fc865 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs @@ -3,21 +3,29 @@ using osu.Framework.Input.Bindings; using osu.Game.Graphics; +using osu.Game.Overlays.Settings; namespace osu.Game.Overlays.KeyBinding { - public class GlobalKeyBindingsSection : KeyBindingsSection + public class GlobalKeyBindingsSection : SettingsSection { - private readonly string name; + public override FontAwesome Icon => FontAwesome.fa_osu_hot; + public override string Header => "Global"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail; - public override string Header => name; - - public GlobalKeyBindingsSection(KeyBindingInputManager manager, string name) + public GlobalKeyBindingsSection(KeyBindingInputManager manager) { - this.name = name; + Add(new DefaultBindingsSubsection(manager)); + } - Defaults = manager.DefaultKeyBindings; + private class DefaultBindingsSubsection : KeyBindingsSubsection + { + protected override string Header => string.Empty; + + public DefaultBindingsSubsection(KeyBindingInputManager manager) + : base(0) + { + Defaults = manager.DefaultKeyBindings; + } } } } \ No newline at end of file diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs similarity index 57% rename from osu.Game/Overlays/KeyBinding/KeyBindingsSection.cs rename to osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index 44c28ee9c8..9bbbf539c5 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -1,7 +1,6 @@ // 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.Allocation; @@ -12,38 +11,34 @@ using OpenTK; namespace osu.Game.Overlays.KeyBinding { - public abstract class KeyBindingsSection : SettingsSection + public abstract class KeyBindingsSubsection : SettingsSubsection { protected IEnumerable Defaults; protected RulesetInfo Ruleset; - protected KeyBindingsSection() + private readonly int variant; + + protected KeyBindingsSubsection(int variant) { + this.variant = variant; + FlowContent.Spacing = new Vector2(0, 1); } [BackgroundDependencyLoader] private void load(KeyBindingStore store) { - var enumType = Defaults?.FirstOrDefault()?.Action?.GetType(); - - if (enumType == null) return; - - // for now let's just assume a variant of zero. - // this will need to be implemented in a better way in the future. - int? variant = null; - if (Ruleset != null) - variant = 0; - var bindings = store.Query(Ruleset?.ID, variant); - foreach (Enum v in Enum.GetValues(enumType)) + foreach (var defaultBinding in Defaults) + { // one row per valid action. - Add(new KeyBindingRow(v, bindings.Where(b => b.Action.Equals((int)(object)v))) + Add(new KeyBindingRow(defaultBinding.Action, bindings.Where(b => b.Action.Equals((int)defaultBinding.Action))) { AllowMainMouseButtons = Ruleset != null }); + } } } } \ No newline at end of file diff --git a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs index 20941115e3..885e149cb2 100644 --- a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs @@ -2,20 +2,26 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Graphics; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets; namespace osu.Game.Overlays.KeyBinding { - public class RulesetBindingsSection : KeyBindingsSection + public class RulesetBindingsSection : SettingsSection { - public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail; - public override string Header => Ruleset.Name; + public override FontAwesome Icon => FontAwesome.fa_osu_hot; + public override string Header => ruleset.Name; + + private readonly RulesetInfo ruleset; public RulesetBindingsSection(RulesetInfo ruleset) { - Ruleset = ruleset; + this.ruleset = ruleset; - Defaults = ruleset.CreateInstance().GetDefaultKeyBindings(); + var r = ruleset.CreateInstance(); + + foreach (var variant in r.AvailableVariants) + Add(new VariantBindingsSubsection(ruleset, variant)); } } } \ No newline at end of file diff --git a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs new file mode 100644 index 0000000000..f58cb632de --- /dev/null +++ b/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets; + +namespace osu.Game.Overlays.KeyBinding +{ + public class VariantBindingsSubsection : KeyBindingsSubsection + { + protected override string Header => variant > 0 ? $"Variant: {variant}" : string.Empty; + + private readonly int variant; + + public VariantBindingsSubsection(RulesetInfo ruleset, int variant) + : base(variant) + { + this.variant = variant; + + Ruleset = ruleset; + Defaults = ruleset.CreateInstance().GetDefaultKeyBindings(variant); + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/KeyBindingOverlay.cs b/osu.Game/Overlays/KeyBindingOverlay.cs index 827d361099..72c653030c 100644 --- a/osu.Game/Overlays/KeyBindingOverlay.cs +++ b/osu.Game/Overlays/KeyBindingOverlay.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader(permitNulls: true)] private void load(RulesetStore rulesets, GlobalKeyBindingInputManager global) { - AddSection(new GlobalKeyBindingsSection(global, "Global")); + AddSection(new GlobalKeyBindingsSection(global)); foreach (var ruleset in rulesets.AllRulesets) AddSection(new RulesetBindingsSection(ruleset)); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 11b99c1796..32a8df6357 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -105,9 +105,10 @@ - + + From 0e41fc8842d3c870c47acd046868d3bd948417c0 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 12:49:50 +0900 Subject: [PATCH 25/71] Add mania bindings. --- osu.Game.Rulesets.Mania/ManiaInputManager.cs | 26 +++++++++++-- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 37 +++++++++++++++++++ .../UI/ManiaRulesetContainer.cs | 2 +- 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaInputManager.cs b/osu.Game.Rulesets.Mania/ManiaInputManager.cs index b608e4d8d6..0fac1bef10 100644 --- a/osu.Game.Rulesets.Mania/ManiaInputManager.cs +++ b/osu.Game.Rulesets.Mania/ManiaInputManager.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.ComponentModel; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.UI; @@ -8,14 +9,33 @@ namespace osu.Game.Rulesets.Mania { public class ManiaInputManager : RulesetInputManager { - public ManiaInputManager(RulesetInfo ruleset) - : base(ruleset, 0, SimultaneousBindingMode.Unique) + public ManiaInputManager(RulesetInfo ruleset, int variant) + : base(ruleset, variant, SimultaneousBindingMode.Unique) { } } public enum ManiaAction { - // placeholder + [Description("Key 1")] + Key1, + [Description("Key 2")] + Key2, + [Description("Key 3")] + Key3, + [Description("Key 4")] + Key4, + [Description("Key 5")] + Key5, + [Description("Key 6")] + Key6, + [Description("Key 7")] + Key7, + [Description("Key 8")] + Key8, + [Description("Key 9")] + Key9, + [Description("Special")] + Special } } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index c7809f83ed..f073d5a04c 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -8,6 +8,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using System.Collections.Generic; using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Scoring; @@ -120,5 +121,41 @@ namespace osu.Game.Rulesets.Mania : base(rulesetInfo) { } + + public override IEnumerable AvailableVariants => new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + public override IEnumerable GetDefaultKeyBindings(int variant = 0) + { + var leftKeys = new[] + { + InputKey.A, + InputKey.S, + InputKey.D, + InputKey.F + }; + + var rightKeys = new[] + { + InputKey.J, + InputKey.K, + InputKey.L, + InputKey.Semicolon + }; + + ManiaAction currentKey = ManiaAction.Key1; + + var bindings = new List(); + + for (int i = 0; i < variant / 2; i++) + bindings.Add(new KeyBinding(leftKeys[leftKeys.Length - 1 - i], currentKey++)); + + for (int i = 0; i < variant / 2; i++) + bindings.Add(new KeyBinding(rightKeys[i], currentKey++)); + + if (variant % 2 == 1) + bindings.Add(new KeyBinding(InputKey.Space, ManiaAction.Special)); + + return bindings; + } } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index 0e750a348e..782fa5f69e 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Mania.UI public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); - public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo); + public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, availableColumns); protected override BeatmapConverter CreateBeatmapConverter() { From 84dd1283dec2e73b1c96d6deabd9ed1906156532 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 13:39:51 +0900 Subject: [PATCH 26/71] Fix nullref. --- osu.Game/Rulesets/UI/RulesetContainer.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index ebf570fecc..7fed5c2225 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -249,7 +249,7 @@ namespace osu.Game.Rulesets.UI public Playfield Playfield { get; private set; } protected override Container Content => content; - private readonly Container content; + private Container content; private readonly List> drawableObjects = new List>(); @@ -261,6 +261,11 @@ namespace osu.Game.Rulesets.UI /// Whether to assume the beatmap is for the current ruleset. protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) : base(ruleset, beatmap, isForCurrentRuleset) + { + } + + [BackgroundDependencyLoader] + private void load() { InputManager.Add(content = new Container { @@ -269,11 +274,6 @@ namespace osu.Game.Rulesets.UI }); AddInternal(InputManager); - } - - [BackgroundDependencyLoader] - private void load() - { KeyBindingInputManager.Add(Playfield = CreatePlayfield()); loadObjects(); From d8dfcc614a4a182da84be71f6ca579cc79804abc Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 13:42:11 +0900 Subject: [PATCH 27/71] Make mania work with key bindings --- .../Visual/TestCaseManiaHitObjects.cs | 7 +- .../Visual/TestCaseManiaPlayfield.cs | 178 +++++++++--------- .../Objects/Drawables/DrawableHoldNote.cs | 51 +++-- .../Drawables/DrawableManiaHitObject.cs | 10 +- .../Objects/Drawables/DrawableNote.cs | 22 +-- osu.Game.Rulesets.Mania/UI/Column.cs | 34 ++-- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 25 +-- .../UI/ManiaRulesetContainer.cs | 8 +- 8 files changed, 146 insertions(+), 189 deletions(-) diff --git a/osu.Desktop.Tests/Visual/TestCaseManiaHitObjects.cs b/osu.Desktop.Tests/Visual/TestCaseManiaHitObjects.cs index 7dcc48c778..76235bbf19 100644 --- a/osu.Desktop.Tests/Visual/TestCaseManiaHitObjects.cs +++ b/osu.Desktop.Tests/Visual/TestCaseManiaHitObjects.cs @@ -3,6 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using OpenTK; @@ -40,8 +41,8 @@ namespace osu.Desktop.Tests.Visual RelativeChildSize = new Vector2(1, 10000), Children = new[] { - new DrawableNote(new Note { StartTime = 5000 }) { AccentColour = Color4.Red }, - new DrawableNote(new Note { StartTime = 6000 }) { AccentColour = Color4.Red } + new DrawableNote(new Note { StartTime = 5000 }, ManiaAction.Key1) { AccentColour = Color4.Red }, + new DrawableNote(new Note { StartTime = 6000 }, ManiaAction.Key1) { AccentColour = Color4.Red } } } } @@ -66,7 +67,7 @@ namespace osu.Desktop.Tests.Visual { StartTime = 5000, Duration = 1000 - }) { AccentColour = Color4.Red } + }, ManiaAction.Key1) { AccentColour = Color4.Red } } } } diff --git a/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs index ed0e5d81e9..c52594930e 100644 --- a/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs +++ b/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs @@ -3,109 +3,34 @@ using System; using System.Linq; -using osu.Framework.Configuration; +using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; -using osu.Framework.Input; using osu.Framework.Timing; +using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Timing; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Timing; using OpenTK; -using OpenTK.Input; +using osu.Game.Rulesets; namespace osu.Desktop.Tests.Visual { internal class TestCaseManiaPlayfield : OsuTestCase { + private const double start_time = 500; + private const double duration = 500; + public override string Description => @"Mania playfield"; protected override double TimePerAction => 200; + private RulesetInfo maniaRuleset; + public TestCaseManiaPlayfield() { - Action createPlayfield = (cols, pos) => - { - Clear(); - Add(new ManiaPlayfield(cols) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - SpecialColumnPosition = pos, - Scale = new Vector2(1, -1) - }); - }; - - const double start_time = 500; - const double duration = 500; - - Func createTimingChange = (time, gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time) - { - TimingPoint = { BeatLength = 1000 } - }, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic); - - Action createPlayfieldWithNotes = gravity => - { - Clear(); - - var rateAdjustClock = new StopwatchClock(true) { Rate = 1 }; - - ManiaPlayfield playField; - Add(playField = new ManiaPlayfield(4) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Scale = new Vector2(1, -1), - Clock = new FramedClock(rateAdjustClock) - }); - - if (!gravity) - playField.Columns.ForEach(c => c.Add(createTimingChange(0, false))); - - for (double t = start_time; t <= start_time + duration; t += 100) - { - if (gravity) - playField.Columns.ElementAt(0).Add(createTimingChange(t, true)); - - playField.Add(new DrawableNote(new Note - { - StartTime = t, - Column = 0 - }, new Bindable(Key.D))); - - if (gravity) - playField.Columns.ElementAt(3).Add(createTimingChange(t, true)); - - playField.Add(new DrawableNote(new Note - { - StartTime = t, - Column = 3 - }, new Bindable(Key.K))); - } - - if (gravity) - playField.Columns.ElementAt(1).Add(createTimingChange(start_time, true)); - - playField.Add(new DrawableHoldNote(new HoldNote - { - StartTime = start_time, - Duration = duration, - Column = 1 - }, new Bindable(Key.F))); - - if (gravity) - playField.Columns.ElementAt(2).Add(createTimingChange(start_time, true)); - - playField.Add(new DrawableHoldNote(new HoldNote - { - StartTime = start_time, - Duration = duration, - Column = 2 - }, new Bindable(Key.J))); - }; - AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal)); AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal)); AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left)); @@ -122,21 +47,94 @@ namespace osu.Desktop.Tests.Visual AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction)); } - private void triggerKeyDown(Column column) + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) { - column.TriggerOnKeyDown(new InputState(), new KeyDownEventArgs + maniaRuleset = rulesets.GetRuleset(3); + } + + private SpeedAdjustmentContainer createTimingChange(double time, bool gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time) + { + TimingPoint = { BeatLength = 1000 } + }, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic); + + private void createPlayfield(int cols, SpecialColumnPosition specialPos) + { + Clear(); + + var inputManager = new ManiaInputManager(maniaRuleset, cols) { RelativeSizeAxes = Axes.Both }; + Add(inputManager); + + inputManager.Add(new ManiaPlayfield(cols) { - Key = column.Key, - Repeat = false + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + SpecialColumnPosition = specialPos, + Scale = new Vector2(1, -1) }); } - private void triggerKeyUp(Column column) + private void createPlayfieldWithNotes(bool gravity) { - column.TriggerOnKeyUp(new InputState(), new KeyUpEventArgs + Clear(); + + var rateAdjustClock = new StopwatchClock(true) { Rate = 1 }; + + var inputManager = new ManiaInputManager(maniaRuleset, 4) { RelativeSizeAxes = Axes.Both }; + Add(inputManager); + + ManiaPlayfield playField; + inputManager.Add(playField = new ManiaPlayfield(4) { - Key = column.Key + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(1, -1), + Clock = new FramedClock(rateAdjustClock) }); + + if (!gravity) + playField.Columns.ForEach(c => c.Add(createTimingChange(0, false))); + + for (double t = start_time; t <= start_time + duration; t += 100) + { + if (gravity) + playField.Columns.ElementAt(0).Add(createTimingChange(t, true)); + + playField.Add(new DrawableNote(new Note + { + StartTime = t, + Column = 0 + }, ManiaAction.Key1)); + + if (gravity) + playField.Columns.ElementAt(3).Add(createTimingChange(t, true)); + + playField.Add(new DrawableNote(new Note + { + StartTime = t, + Column = 3 + }, ManiaAction.Key4)); + } + + if (gravity) + playField.Columns.ElementAt(1).Add(createTimingChange(start_time, true)); + + playField.Add(new DrawableHoldNote(new HoldNote + { + StartTime = start_time, + Duration = duration, + Column = 1 + }, ManiaAction.Key2)); + + if (gravity) + playField.Columns.ElementAt(2).Add(createTimingChange(start_time, true)); + + playField.Add(new DrawableHoldNote(new HoldNote + { + StartTime = start_time, + Duration = duration, + Column = 2 + }, ManiaAction.Key3)); } } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 17b0b0a607..e06f71cb64 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -5,20 +5,18 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using OpenTK.Graphics; -using osu.Framework.Configuration; -using OpenTK.Input; -using osu.Framework.Input; using OpenTK; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Mania.Judgements; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Input.Bindings; namespace osu.Game.Rulesets.Mania.Objects.Drawables { /// /// Visualises a hit object. /// - public class DrawableHoldNote : DrawableManiaHitObject + public class DrawableHoldNote : DrawableManiaHitObject, IKeyBindingHandler { private readonly DrawableNote head; private readonly DrawableNote tail; @@ -36,8 +34,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// private bool hasBroken; - public DrawableHoldNote(HoldNote hitObject, Bindable key = null) - : base(hitObject, key) + public DrawableHoldNote(HoldNote hitObject, ManiaAction action) + : base(hitObject, action) { RelativeSizeAxes = Axes.Both; Height = (float)HitObject.Duration; @@ -58,12 +56,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime), RelativeChildSize = new Vector2(1, (float)HitObject.Duration) }, - head = new DrawableHeadNote(this, key) + head = new DrawableHeadNote(this, action) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre }, - tail = new DrawableTailNote(this, key) + tail = new DrawableTailNote(this, action) { Anchor = Anchor.BottomCentre, Origin = Anchor.TopCentre @@ -106,16 +104,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + public bool OnPressed(ManiaAction action) { - // Make sure the keypress happened within the body of the hold note + // Make sure the action happened within the body of the hold note if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime) return false; - if (args.Key != Key) - return false; - - if (args.Repeat) + if (action != Action) return false; // The user has pressed during the body of the hold note, after the head note and its hit windows have passed @@ -126,13 +121,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables return true; } - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) + public bool OnReleased(ManiaAction action) { // Make sure that the user started holding the key during the hold note if (!holdStartTime.HasValue) return false; - if (args.Key != Key) + if (action != Action) return false; holdStartTime = null; @@ -151,8 +146,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { private readonly DrawableHoldNote holdNote; - public DrawableHeadNote(DrawableHoldNote holdNote, Bindable key = null) - : base(holdNote.HitObject.Head, key) + public DrawableHeadNote(DrawableHoldNote holdNote, ManiaAction action) + : base(holdNote.HitObject.Head, action) { this.holdNote = holdNote; @@ -160,9 +155,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Y = 0; } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + public override bool OnPressed(ManiaAction action) { - if (!base.OnKeyDown(state, args)) + if (!base.OnPressed(action)) return false; // We only want to trigger a holding state from the head if the head has received a judgement @@ -188,8 +183,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { private readonly DrawableHoldNote holdNote; - public DrawableTailNote(DrawableHoldNote holdNote, Bindable key = null) - : base(holdNote.HitObject.Tail, key) + public DrawableTailNote(DrawableHoldNote holdNote, ManiaAction action) + : base(holdNote.HitObject.Tail, action) { this.holdNote = holdNote; @@ -210,7 +205,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables tailJudgement.HasBroken = holdNote.hasBroken; } - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) + public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down + + public override bool OnReleased(ManiaAction action) { // Make sure that the user started holding the key during the hold note if (!holdNote.holdStartTime.HasValue) @@ -219,7 +216,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (Judgement.Result != HitResult.None) return false; - if (args.Key != Key) + if (action != Action) return false; UpdateJudgement(true); @@ -227,12 +224,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables // Handled by the hold note, which will set holding = false return false; } - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - // Tail doesn't handle key down - return false; - } } } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 10dc607ec3..bfef05ea07 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -2,8 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using OpenTK.Graphics; -using OpenTK.Input; -using osu.Framework.Configuration; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Objects.Drawables; @@ -15,17 +13,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// /// The key that will trigger input for this hit object. /// - protected Bindable Key { get; private set; } = new Bindable(); + protected ManiaAction Action { get; } public new TObject HitObject; - protected DrawableManiaHitObject(TObject hitObject, Bindable key = null) + protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null) : base(hitObject) { HitObject = hitObject; - if (key != null) - Key.BindTo(key); + if (action != null) + Action = action.Value; } public override Color4 AccentColour diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 9322fed3eb..c201ab7bd0 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -3,10 +3,8 @@ using System; using OpenTK.Graphics; -using OpenTK.Input; -using osu.Framework.Configuration; using osu.Framework.Graphics; -using osu.Framework.Input; +using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Objects.Drawables; @@ -16,12 +14,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables /// /// Visualises a hit object. /// - public class DrawableNote : DrawableManiaHitObject + public class DrawableNote : DrawableManiaHitObject, IKeyBindingHandler { private readonly NotePiece headPiece; - public DrawableNote(Note hitObject, Bindable key = null) - : base(hitObject, key) + public DrawableNote(Note hitObject, ManiaAction action) + : base(hitObject, action) { RelativeSizeAxes = Axes.X; Height = 100; @@ -81,18 +79,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables } } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + public virtual bool OnPressed(ManiaAction action) { - if (Judgement.Result != HitResult.None) - return false; - - if (args.Key != Key) - return false; - - if (args.Repeat) + if (action != Action) return false; return UpdateJudgement(true); } + + public virtual bool OnReleased(ManiaAction action) => false; } } diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 9fbc9ba5e7..a8f5b4919d 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -3,17 +3,15 @@ using OpenTK; using OpenTK.Graphics; -using OpenTK.Input; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Colour; -using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using System; -using osu.Framework.Configuration; +using osu.Framework.Input.Bindings; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Judgements; @@ -32,10 +30,7 @@ namespace osu.Game.Rulesets.Mania.UI private const float column_width = 45; private const float special_column_width = 70; - /// - /// The key that will trigger input actions for this column and hit objects contained inside it. - /// - public Bindable Key = new Bindable(); + public ManiaAction Action; private readonly Box background; private readonly Container hitTargetBar; @@ -101,8 +96,8 @@ namespace osu.Game.Rulesets.Mania.UI // For column lighting, we need to capture input events before the notes new InputTarget { - KeyDown = onKeyDown, - KeyUp = onKeyUp + Pressed = onPressed, + Released = onReleased } } }, @@ -199,12 +194,9 @@ namespace osu.Game.Rulesets.Mania.UI HitObjects.Add(hitObject); } - private bool onKeyDown(InputState state, KeyDownEventArgs args) + private bool onPressed(ManiaAction action) { - if (args.Repeat) - return false; - - if (args.Key == Key) + if (action == Action) { background.FadeTo(background.Alpha + 0.2f, 50, Easing.OutQuint); keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint); @@ -213,9 +205,9 @@ namespace osu.Game.Rulesets.Mania.UI return false; } - private bool onKeyUp(InputState state, KeyUpEventArgs args) + private bool onReleased(ManiaAction action) { - if (args.Key == Key) + if (action == Action) { background.FadeTo(0.2f, 800, Easing.OutQuart); keyIcon.ScaleTo(1f, 400, Easing.OutQuart); @@ -227,10 +219,10 @@ namespace osu.Game.Rulesets.Mania.UI /// /// This is a simple container which delegates various input events that have to be captured before the notes. /// - private class InputTarget : Container + private class InputTarget : Container, IKeyBindingHandler { - public Func KeyDown; - public Func KeyUp; + public Func Pressed; + public Func Released; public InputTarget() { @@ -239,8 +231,8 @@ namespace osu.Game.Rulesets.Mania.UI Alpha = 0; } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => KeyDown?.Invoke(state, args) ?? false; - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => KeyUp?.Invoke(state, args) ?? false; + public bool OnPressed(ManiaAction action) => Pressed?.Invoke(action) ?? false; + public bool OnReleased(ManiaAction action) => Released?.Invoke(action) ?? false; } } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 0c9351cad2..be14585354 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -122,12 +122,16 @@ namespace osu.Game.Rulesets.Mania.UI } }; + var currentAction = ManiaAction.Key1; for (int i = 0; i < columnCount; i++) { var c = new Column(); c.Reversed.BindTo(Reversed); c.VisibleTimeRange.BindTo(VisibleTimeRange); + c.IsSpecial = isSpecialColumn(i); + c.Action = c.IsSpecial ? ManiaAction.Special : currentAction++; + columns.Add(c); AddNested(c); } @@ -145,15 +149,11 @@ namespace osu.Game.Rulesets.Mania.UI specialColumnColour = colours.BlueDark; // Set the special column + colour + key - for (int i = 0; i < columnCount; i++) + foreach (var column in Columns) { - Column column = Columns.ElementAt(i); - column.IsSpecial = isSpecialColumn(i); - if (!column.IsSpecial) continue; - column.Key.Value = Key.Space; column.AccentColour = specialColumnColour; } @@ -167,21 +167,6 @@ namespace osu.Game.Rulesets.Mania.UI nonSpecialColumns[i].AccentColour = colour; nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour; } - - // We'll set the keys for non-special columns in another separate loop because it's not mirrored like the above colours - // Todo: This needs to go when we get to bindings and use Button1, ..., ButtonN instead - for (int i = 0; i < nonSpecialColumns.Count; i++) - { - Column column = nonSpecialColumns[i]; - - int keyOffset = default_keys.Length / 2 - nonSpecialColumns.Count / 2 + i; - if (keyOffset >= 0 && keyOffset < default_keys.Length) - column.Key.Value = default_keys[keyOffset]; - else - // There is no default key defined for this column. Let's set this to Unknown for now - // however note that this will be gone after bindings are in place - column.Key.Value = Key.Unknown; - } } /// diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index 782fa5f69e..5a3da6d074 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -5,9 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using OpenTK; -using OpenTK.Input; using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Input; @@ -109,15 +107,15 @@ namespace osu.Game.Rulesets.Mania.UI protected override DrawableHitObject GetVisualRepresentation(ManiaHitObject h) { - Bindable key = Playfield.Columns.ElementAt(h.Column).Key; + ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action; var holdNote = h as HoldNote; if (holdNote != null) - return new DrawableHoldNote(holdNote, key); + return new DrawableHoldNote(holdNote, action); var note = h as Note; if (note != null) - return new DrawableNote(note, key); + return new DrawableNote(note, action); return null; } From 68e7cf854f8798568379d1d78a434588cafa4dd5 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 14:19:14 +0900 Subject: [PATCH 28/71] Add variant names. --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 ++ .../KeyBinding/VariantBindingsSubsection.cs | 13 +++++++------ osu.Game/Rulesets/Ruleset.cs | 7 +++++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index f073d5a04c..6c356195b2 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -157,5 +157,7 @@ namespace osu.Game.Rulesets.Mania return bindings; } + + public override string GetVariantName(int variant) => $"{variant}K"; } } diff --git a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs index f58cb632de..dca5f53b4a 100644 --- a/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/VariantBindingsSubsection.cs @@ -7,17 +7,18 @@ namespace osu.Game.Overlays.KeyBinding { public class VariantBindingsSubsection : KeyBindingsSubsection { - protected override string Header => variant > 0 ? $"Variant: {variant}" : string.Empty; - - private readonly int variant; + protected override string Header => variantName; + private readonly string variantName; public VariantBindingsSubsection(RulesetInfo ruleset, int variant) : base(variant) { - this.variant = variant; - Ruleset = ruleset; - Defaults = ruleset.CreateInstance().GetDefaultKeyBindings(variant); + + var rulesetInstance = ruleset.CreateInstance(); + + variantName = rulesetInstance.GetVariantName(variant); + Defaults = rulesetInstance.GetDefaultKeyBindings(variant); } } } \ No newline at end of file diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 32d958b462..c54aeb7852 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -63,5 +63,12 @@ namespace osu.Game.Rulesets /// A variant. /// A list of valid s. public virtual IEnumerable GetDefaultKeyBindings(int variant = 0) => new KeyBinding[] { }; + + /// + /// Gets the name for a key binding variant. This is used for display in the settings overlay. + /// + /// The variant. + /// A descriptive name of the variant. + public virtual string GetVariantName(int variant) => string.Empty; } } From e777ccc3398a155b33423bf3f056d8d075142368 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Aug 2017 14:38:26 +0900 Subject: [PATCH 29/71] Avoid threading conflicts when resetting the database in unit tests --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index ecaf0db096..eb456d7a63 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -13,7 +13,6 @@ using osu.Framework.Platform; using osu.Game.IPC; using osu.Framework.Allocation; using osu.Game.Beatmaps; -using osu.Game.Rulesets; namespace osu.Game.Tests.Beatmaps.IO { @@ -98,16 +97,14 @@ namespace osu.Game.Tests.Beatmaps.IO private OsuGameBase loadOsu(GameHost host) { + host.Storage.DeleteDatabase(@"client"); + var osu = new OsuGameBase(); Task.Run(() => host.Run(osu)); while (!osu.IsLoaded) Thread.Sleep(1); - //reset beatmap database (sqlite and storage backing) - osu.Dependencies.Get().Reset(); - osu.Dependencies.Get().Reset(); - return osu; } From 751bd3432e3dc4dee25bbb097a7dc019619d53dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Aug 2017 14:48:58 +0900 Subject: [PATCH 30/71] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 74f644bad0..e2d41820b3 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 74f644bad039606e242d8782014d8c249a38b6a3 +Subproject commit e2d41820b3795d97f8510a18fad5e42caeb23f0a From 0c60563fda957d7efc27fd0b62a6b8a592d9ef62 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 15:24:08 +0900 Subject: [PATCH 31/71] Update framework. --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 1fd9259e86..e2d41820b3 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 1fd9259e86379838dbbc43c12a2238df83800fc0 +Subproject commit e2d41820b3795d97f8510a18fad5e42caeb23f0a From 7639e096d865cb044186df0fc4ca4d085b4e9d5c Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 15:30:17 +0900 Subject: [PATCH 32/71] No longer flip legacy replay mouse positions. --- osu.Game/Rulesets/Scoring/ScoreStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreStore.cs b/osu.Game/Rulesets/Scoring/ScoreStore.cs index c6421522b4..c06d31e38f 100644 --- a/osu.Game/Rulesets/Scoring/ScoreStore.cs +++ b/osu.Game/Rulesets/Scoring/ScoreStore.cs @@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Scoring frames.Add(new ReplayFrame( lastTime, float.Parse(split[1]), - 384 - float.Parse(split[2]), + float.Parse(split[2]), (ReplayButtonState)int.Parse(split[3]) )); } From 4395ea3850baaf67047cc910c4e60a028e76af5c Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 15:37:22 +0900 Subject: [PATCH 33/71] Add applied suggestions. --- osu.Game.Rulesets.Mania/ManiaInputManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaInputManager.cs b/osu.Game.Rulesets.Mania/ManiaInputManager.cs index 0fac1bef10..a574bc75ec 100644 --- a/osu.Game.Rulesets.Mania/ManiaInputManager.cs +++ b/osu.Game.Rulesets.Mania/ManiaInputManager.cs @@ -17,8 +17,10 @@ namespace osu.Game.Rulesets.Mania public enum ManiaAction { + [Description("Special")] + Special, [Description("Key 1")] - Key1, + Key1 = 10, [Description("Key 2")] Key2, [Description("Key 3")] @@ -34,8 +36,6 @@ namespace osu.Game.Rulesets.Mania [Description("Key 8")] Key8, [Description("Key 9")] - Key9, - [Description("Special")] - Special + Key9 } } From bdd0a51c6e178ca0ccac027087f8f576037513f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Aug 2017 16:10:10 +0900 Subject: [PATCH 34/71] Fix left keys setting defaults in incorrect order --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 6c356195b2..ed46da4f85 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -146,8 +146,8 @@ namespace osu.Game.Rulesets.Mania var bindings = new List(); - for (int i = 0; i < variant / 2; i++) - bindings.Add(new KeyBinding(leftKeys[leftKeys.Length - 1 - i], currentKey++)); + for (int i = leftKeys.Length - variant / 2; i < leftKeys.Length; i++) + bindings.Add(new KeyBinding(leftKeys[i], currentKey++)); for (int i = 0; i < variant / 2; i++) bindings.Add(new KeyBinding(rightKeys[i], currentKey++)); From efdd5eb152d532adeed652e8d63f8b930b5331f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Aug 2017 16:10:31 +0900 Subject: [PATCH 35/71] Fix global key bindings being fetched with a non-null variant --- osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs | 2 +- osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs index 77de9fc865..8ebd4ac545 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.KeyBinding protected override string Header => string.Empty; public DefaultBindingsSubsection(KeyBindingInputManager manager) - : base(0) + : base(null) { Defaults = manager.DefaultKeyBindings; } diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index 9bbbf539c5..38a5b1a171 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -17,9 +17,9 @@ namespace osu.Game.Overlays.KeyBinding protected RulesetInfo Ruleset; - private readonly int variant; + private readonly int? variant; - protected KeyBindingsSubsection(int variant) + protected KeyBindingsSubsection(int? variant) { this.variant = variant; From 42e6865493f3e87f9b1be8659d8f074b42e92b0c Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 15:35:31 +0900 Subject: [PATCH 36/71] Flip hit objects. --- osu.Game.Rulesets.Osu/Mods/OsuMod.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs index 3b0cfc1ef1..6bc9f868ac 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs @@ -7,8 +7,12 @@ using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using System; +using System.Collections.Generic; using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using OpenTK; namespace osu.Game.Rulesets.Osu.Mods { @@ -28,10 +32,23 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1.06; } - public class OsuModHardRock : ModHardRock + public class OsuModHardRock : ModHardRock, IApplicableMod { public override double ScoreMultiplier => 1.06; public override bool Ranked => true; + + public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) + { + rulesetContainer.Objects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, 384 - h.Y)); + rulesetContainer.Objects.OfType().ForEach(s => + { + var newControlPoints = new List(); + s.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, 384 - c.Y))); + + s.ControlPoints = newControlPoints; + s.Curve?.Calculate(); // Recalculate the slider curve + }); + } } public class OsuModSuddenDeath : ModSuddenDeath From 8ddc13e394fdaa355152f9067c0790d3f09fba8a Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 16:48:13 +0900 Subject: [PATCH 37/71] Fix replays not loading. --- osu.Game/Rulesets/UI/RulesetContainer.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 2f1cb915f3..8a12286bc6 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.UI /// Sets a replay to be used, overriding local input. /// /// The replay, null for local input. - public void SetReplay(Replay replay) + public virtual void SetReplay(Replay replay) { Replay = replay; InputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; @@ -272,6 +272,11 @@ namespace osu.Game.Rulesets.UI KeyBindingInputManager.Add(Playfield = CreatePlayfield()); loadObjects(); + } + + public override void SetReplay(Replay replay) + { + base.SetReplay(replay); if (InputManager?.ReplayInputHandler != null) InputManager.ReplayInputHandler.ToScreenSpace = Playfield.ScaledContent.ToScreenSpace; From 1972b78cb13d8bea10d7392bd4a63be6efef1a92 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Aug 2017 15:28:33 +0900 Subject: [PATCH 38/71] Make OsuTestCases use new non-realtime option --- osu.Desktop.Tests/Visual/OsuTestCase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop.Tests/Visual/OsuTestCase.cs b/osu.Desktop.Tests/Visual/OsuTestCase.cs index e54f7dbeb5..e366aecb21 100644 --- a/osu.Desktop.Tests/Visual/OsuTestCase.cs +++ b/osu.Desktop.Tests/Visual/OsuTestCase.cs @@ -14,7 +14,7 @@ namespace osu.Desktop.Tests.Visual [Test] public override void RunTest() { - using (var host = new HeadlessGameHost()) + using (var host = new HeadlessGameHost(realtime: false)) host.Run(new OsuTestCaseTestRunner(this)); } From 022063f15766d147690f32180254cae5e806d33b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Aug 2017 16:51:09 +0900 Subject: [PATCH 39/71] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index e2d41820b3..1ba1e8ef1e 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit e2d41820b3795d97f8510a18fad5e42caeb23f0a +Subproject commit 1ba1e8ef1e5ec0466632be02492023a081cb85ab From 56292bd7f0b1ee64a5fe7d24af2f36f47d1c98c1 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 16:54:06 +0900 Subject: [PATCH 40/71] Use constant definition. --- osu.Game.Rulesets.Osu/Mods/OsuMod.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs index 6bc9f868ac..432c6d391c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuMod.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuMod.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using OpenTK; @@ -39,11 +40,11 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) { - rulesetContainer.Objects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, 384 - h.Y)); + rulesetContainer.Objects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Y)); rulesetContainer.Objects.OfType().ForEach(s => { var newControlPoints = new List(); - s.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, 384 - c.Y))); + s.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y))); s.ControlPoints = newControlPoints; s.Curve?.Calculate(); // Recalculate the slider curve From e9dde822a1c29b5cb160965116b895c4ac3baa93 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Aug 2017 19:25:40 +0900 Subject: [PATCH 41/71] Expose triangles in OsuButton for further customisation --- osu.Game/Graphics/UserInterface/OsuButton.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 5650fc6f48..abc1b036bc 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -24,6 +24,8 @@ namespace osu.Game.Graphics.UserInterface private SampleChannel sampleClick; private SampleChannel sampleHover; + protected Triangles Triangles; + public OsuButton() { Height = 40; @@ -52,7 +54,7 @@ namespace osu.Game.Graphics.UserInterface AddRange(new Drawable[] { - new Triangles + Triangles = new Triangles { RelativeSizeAxes = Axes.Both, ColourDark = colours.BlueDarker, @@ -120,4 +122,4 @@ namespace osu.Game.Graphics.UserInterface } } } -} \ No newline at end of file +} From c99ed6fd440fa88906c13757fa9b08f381041083 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Aug 2017 19:26:49 +0900 Subject: [PATCH 42/71] Add reset button Also fixes a regression causing multiple rows to be displayed for a single action --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 13 +++++++ .../KeyBinding/KeyBindingsSubsection.cs | 36 ++++++++++++++++--- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index c182382d70..ee997a2185 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -109,6 +109,17 @@ namespace osu.Game.Overlays.KeyBinding buttons.Add(new KeyButton(b)); } + public void RestoreDefaults() + { + int i = 0; + foreach (var d in Defaults) + { + var button = buttons[i++]; + button.UpdateKeyCombination(d); + store.Update(button.KeyBinding); + } + } + protected override bool OnHover(InputState state) { this.FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); @@ -129,6 +140,8 @@ namespace osu.Game.Overlays.KeyBinding public bool AllowMainMouseButtons; + public IEnumerable Defaults; + private bool isModifier(Key k) => k < Key.F1; protected override bool OnClick(InputState state) => true; diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index 38a5b1a171..bd69403831 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -4,10 +4,14 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; using osu.Game.Input; using osu.Game.Overlays.Settings; using osu.Game.Rulesets; using OpenTK; +using osu.Game.Graphics; namespace osu.Game.Overlays.KeyBinding { @@ -31,14 +35,38 @@ namespace osu.Game.Overlays.KeyBinding { var bindings = store.Query(Ruleset?.ID, variant); - foreach (var defaultBinding in Defaults) + foreach (var defaultGroup in Defaults.GroupBy(d => d.Action)) { // one row per valid action. - Add(new KeyBindingRow(defaultBinding.Action, bindings.Where(b => b.Action.Equals((int)defaultBinding.Action))) + Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => b.Action.Equals((int)defaultGroup.Key))) { - AllowMainMouseButtons = Ruleset != null + AllowMainMouseButtons = Ruleset != null, + Defaults = defaultGroup.Select(d => d.KeyCombination) }); } + + Add(new ResetButton + { + Action = () => Children.OfType().ForEach(k => k.RestoreDefaults()) + }); } } -} \ No newline at end of file + + internal class ResetButton : OsuButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Text = "Reset"; + RelativeSizeAxes = Axes.X; + Margin = new MarginPadding { Top = 5 }; + Height = 20; + + Content.CornerRadius = 5; + + BackgroundColour = colours.PinkDark; + Triangles.ColourDark = colours.PinkDarker; + Triangles.ColourLight = colours.Pink; + } + } +} From 115e5c95aff64557c8c900a76feaa0dac8061e09 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 23 Aug 2017 20:50:03 +0900 Subject: [PATCH 43/71] Make ScrollingPlayfield.Reversed protected; make ManiaPlayfield 'invertible'. --- .../Visual/TestCaseManiaPlayfield.cs | 45 ++++++++++--------- .../Visual/TestCaseScrollingPlayfield.cs | 6 ++- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 17 ++++++- osu.Game/Rulesets/UI/ScrollingPlayfield.cs | 4 +- 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs index c52594930e..fb14bdb3bf 100644 --- a/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs +++ b/osu.Desktop.Tests/Visual/TestCaseManiaPlayfield.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; @@ -13,7 +12,6 @@ using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Timing; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Timing; -using OpenTK; using osu.Game.Rulesets; namespace osu.Desktop.Tests.Visual @@ -39,12 +37,12 @@ namespace osu.Desktop.Tests.Visual AddStep("8 columns", () => createPlayfield(8, SpecialColumnPosition.Normal)); AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left)); AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right)); + AddStep("Reversed", () => createPlayfield(4, SpecialColumnPosition.Normal, true)); AddStep("Notes with input", () => createPlayfieldWithNotes(false)); - AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction)); - + AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(false, true)); AddStep("Notes with gravity", () => createPlayfieldWithNotes(true)); - AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction)); + AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true, true)); } [BackgroundDependencyLoader] @@ -58,23 +56,25 @@ namespace osu.Desktop.Tests.Visual TimingPoint = { BeatLength = 1000 } }, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic); - private void createPlayfield(int cols, SpecialColumnPosition specialPos) + private void createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false) { Clear(); var inputManager = new ManiaInputManager(maniaRuleset, cols) { RelativeSizeAxes = Axes.Both }; Add(inputManager); - inputManager.Add(new ManiaPlayfield(cols) + ManiaPlayfield playfield; + inputManager.Add(playfield = new ManiaPlayfield(cols) { Anchor = Anchor.Centre, Origin = Anchor.Centre, - SpecialColumnPosition = specialPos, - Scale = new Vector2(1, -1) + SpecialColumnPosition = specialPos }); + + playfield.Inverted.Value = inverted; } - private void createPlayfieldWithNotes(bool gravity) + private void createPlayfieldWithNotes(bool gravity, bool inverted = false) { Clear(); @@ -83,33 +83,34 @@ namespace osu.Desktop.Tests.Visual var inputManager = new ManiaInputManager(maniaRuleset, 4) { RelativeSizeAxes = Axes.Both }; Add(inputManager); - ManiaPlayfield playField; - inputManager.Add(playField = new ManiaPlayfield(4) + ManiaPlayfield playfield; + inputManager.Add(playfield = new ManiaPlayfield(4) { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Scale = new Vector2(1, -1), Clock = new FramedClock(rateAdjustClock) }); + playfield.Inverted.Value = inverted; + if (!gravity) - playField.Columns.ForEach(c => c.Add(createTimingChange(0, false))); + playfield.Columns.ForEach(c => c.Add(createTimingChange(0, false))); for (double t = start_time; t <= start_time + duration; t += 100) { if (gravity) - playField.Columns.ElementAt(0).Add(createTimingChange(t, true)); + playfield.Columns.ElementAt(0).Add(createTimingChange(t, true)); - playField.Add(new DrawableNote(new Note + playfield.Add(new DrawableNote(new Note { StartTime = t, Column = 0 }, ManiaAction.Key1)); if (gravity) - playField.Columns.ElementAt(3).Add(createTimingChange(t, true)); + playfield.Columns.ElementAt(3).Add(createTimingChange(t, true)); - playField.Add(new DrawableNote(new Note + playfield.Add(new DrawableNote(new Note { StartTime = t, Column = 3 @@ -117,9 +118,9 @@ namespace osu.Desktop.Tests.Visual } if (gravity) - playField.Columns.ElementAt(1).Add(createTimingChange(start_time, true)); + playfield.Columns.ElementAt(1).Add(createTimingChange(start_time, true)); - playField.Add(new DrawableHoldNote(new HoldNote + playfield.Add(new DrawableHoldNote(new HoldNote { StartTime = start_time, Duration = duration, @@ -127,9 +128,9 @@ namespace osu.Desktop.Tests.Visual }, ManiaAction.Key2)); if (gravity) - playField.Columns.ElementAt(2).Add(createTimingChange(start_time, true)); + playfield.Columns.ElementAt(2).Add(createTimingChange(start_time, true)); - playField.Add(new DrawableHoldNote(new HoldNote + playfield.Add(new DrawableHoldNote(new HoldNote { StartTime = start_time, Duration = duration, diff --git a/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs index c8c95da618..444adf6c24 100644 --- a/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs +++ b/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs @@ -64,8 +64,8 @@ namespace osu.Desktop.Tests.Visual AddStep("Reverse direction", () => { - horizontalRulesetContainer.Playfield.Reversed.Toggle(); - verticalRulesetContainer.Playfield.Reversed.Toggle(); + horizontalRulesetContainer.Playfield.Reverse(); + verticalRulesetContainer.Playfield.Reverse(); }); } @@ -210,6 +210,8 @@ namespace osu.Desktop.Tests.Visual content = new Container { RelativeSizeAxes = Axes.Both } }; } + + public void Reverse() => Reversed.Toggle(); } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index be14585354..d3dc920fc6 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -14,6 +14,7 @@ using osu.Framework.Allocation; using OpenTK.Input; using System.Linq; using System.Collections.Generic; +using osu.Framework.Configuration; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Framework.Graphics.Shapes; @@ -45,6 +46,11 @@ namespace osu.Game.Rulesets.Mania.UI } } + /// + /// Whether this playfield should be inverted. This flips everything inside the playfield. + /// + public readonly Bindable Inverted = new Bindable(true); + private readonly FlowContainer columns; public IEnumerable Columns => columns.Children; @@ -64,6 +70,8 @@ namespace osu.Game.Rulesets.Mania.UI if (columnCount <= 0) throw new ArgumentException("Can't have zero or fewer columns."); + Inverted.Value = true; + InternalChildren = new Drawable[] { new Container @@ -126,7 +134,6 @@ namespace osu.Game.Rulesets.Mania.UI for (int i = 0; i < columnCount; i++) { var c = new Column(); - c.Reversed.BindTo(Reversed); c.VisibleTimeRange.BindTo(VisibleTimeRange); c.IsSpecial = isSpecialColumn(i); @@ -135,6 +142,14 @@ namespace osu.Game.Rulesets.Mania.UI columns.Add(c); AddNested(c); } + + Inverted.ValueChanged += invertedChanged; + Inverted.TriggerChange(); + } + + private void invertedChanged(bool newValue) + { + Scale = new Vector2(1, newValue ? -1 : 1); } [BackgroundDependencyLoader] diff --git a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs index 92845c3f3a..1d13e2abb1 100644 --- a/osu.Game/Rulesets/UI/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/ScrollingPlayfield.cs @@ -55,9 +55,9 @@ namespace osu.Game.Rulesets.UI }; /// - /// Whether to reverse the scrolling direction is reversed. + /// Whether to reverse the scrolling direction is reversed. Note that this does _not_ invert the hit objects. /// - public readonly BindableBool Reversed = new BindableBool(); + protected readonly BindableBool Reversed = new BindableBool(); /// /// The container that contains the s and s. From 189988236b91f63b5e374f9289025ac938cebf4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 15:23:17 +0900 Subject: [PATCH 44/71] Move PlayerInputManager logic inside RulesetInputManager --- osu.Game/Rulesets/UI/RulesetContainer.cs | 20 +-- osu.Game/Rulesets/UI/RulesetInputManager.cs | 163 +++++++++++++++++++- osu.Game/Screens/Play/PlayerInputManager.cs | 140 ----------------- osu.Game/osu.Game.csproj | 1 - 4 files changed, 169 insertions(+), 155 deletions(-) delete mode 100644 osu.Game/Screens/Play/PlayerInputManager.cs diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 84cadeb2a1..0a16335c77 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -9,7 +9,6 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Screens.Play; using System; using System.Collections.Generic; using System.Diagnostics; @@ -43,7 +42,7 @@ namespace osu.Game.Rulesets.UI /// /// The input manager for this RulesetContainer. /// - internal readonly PlayerInputManager InputManager = new PlayerInputManager(); + internal IHasReplayHandler ReplayInputManager => (IHasReplayHandler)KeyBindingInputManager; /// /// The key conversion input manager for this RulesetContainer. @@ -58,7 +57,7 @@ namespace osu.Game.Rulesets.UI /// /// Whether we have a replay loaded currently. /// - public bool HasReplayLoaded => InputManager.ReplayInputHandler != null; + public bool HasReplayLoaded => ReplayInputManager.ReplayInputHandler != null; public abstract IEnumerable Objects { get; } @@ -76,11 +75,7 @@ namespace osu.Game.Rulesets.UI internal RulesetContainer(Ruleset ruleset) { Ruleset = ruleset; - } - [BackgroundDependencyLoader] - private void load() - { KeyBindingInputManager = CreateInputManager(); KeyBindingInputManager.RelativeSizeAxes = Axes.Both; } @@ -113,7 +108,7 @@ namespace osu.Game.Rulesets.UI public virtual void SetReplay(Replay replay) { Replay = replay; - InputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; + ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; } } @@ -267,13 +262,12 @@ namespace osu.Game.Rulesets.UI [BackgroundDependencyLoader] private void load() { - InputManager.Add(content = new Container + KeyBindingInputManager.Add(content = new Container { RelativeSizeAxes = Axes.Both, - Children = new[] { KeyBindingInputManager } }); - AddInternal(InputManager); + AddInternal(KeyBindingInputManager); KeyBindingInputManager.Add(Playfield = CreatePlayfield()); loadObjects(); @@ -283,8 +277,8 @@ namespace osu.Game.Rulesets.UI { base.SetReplay(replay); - if (InputManager?.ReplayInputHandler != null) - InputManager.ReplayInputHandler.ToScreenSpace = Playfield.ScaledContent.ToScreenSpace; + if (ReplayInputManager?.ReplayInputHandler != null) + ReplayInputManager.ReplayInputHandler.ToScreenSpace = input => Playfield.ScaledContent.ToScreenSpace(input); } /// diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 6d22b2e91e..bd7e51e937 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -1,20 +1,176 @@ // 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 System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Input; using osu.Framework.Input.Bindings; +using osu.Framework.Timing; +using osu.Game.Configuration; using osu.Game.Input.Bindings; +using osu.Game.Input.Handlers; using osu.Game.Screens.Play; +using OpenTK.Input; namespace osu.Game.Rulesets.UI { - public abstract class RulesetInputManager : DatabasedKeyBindingInputManager, ICanAttachKeyCounter + public abstract class RulesetInputManager : DatabasedKeyBindingInputManager, ICanAttachKeyCounter, IHasReplayHandler where T : struct { protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique) { } + private List lastPressedActions = new List(); + + protected override void HandleNewState(InputState state) + { + base.HandleNewState(state); + + var replayState = state as ReplayInputHandler.ReplayState; + + if (replayState == null) return; + + // Here we handle states specifically coming from a replay source. + // These have extra action information rather than keyboard keys or mouse buttons. + + List newActions = replayState.PressedActions; + + foreach (var released in lastPressedActions.Except(newActions)) + PropagateReleased(KeyBindingInputQueue, released); + + foreach (var pressed in newActions.Except(lastPressedActions)) + PropagatePressed(KeyBindingInputQueue, pressed); + + lastPressedActions = newActions; + } + + private ManualClock clock; + private IFrameBasedClock parentClock; + + private ReplayInputHandler replayInputHandler; + public ReplayInputHandler ReplayInputHandler + { + get + { + return replayInputHandler; + } + set + { + if (replayInputHandler != null) RemoveHandler(replayInputHandler); + + replayInputHandler = value; + UseParentState = replayInputHandler == null; + + if (replayInputHandler != null) + AddHandler(replayInputHandler); + } + } + + private Bindable mouseDisabled; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + //our clock will now be our parent's clock, but we want to replace this to allow manual control. + parentClock = Clock; + + Clock = new FramedClock(clock = new ManualClock + { + CurrentTime = parentClock.CurrentTime, + Rate = parentClock.Rate, + }); + } + + /// + /// Whether we running up-to-date with our parent clock. + /// If not, we will need to keep processing children until we catch up. + /// + private bool requireMoreUpdateLoops; + + /// + /// Whether we in a valid state (ie. should we keep processing children frames). + /// This should be set to false when the replay is, for instance, waiting for future frames to arrive. + /// + private bool validState; + + protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState; + + private bool isAttached => replayInputHandler != null && !UseParentState; + + private const int max_catch_up_updates_per_frame = 50; + + public override bool UpdateSubTree() + { + requireMoreUpdateLoops = true; + validState = true; + + int loops = 0; + + while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame) + if (!base.UpdateSubTree()) + return false; + + return true; + } + + protected override void Update() + { + if (parentClock == null) return; + + clock.Rate = parentClock.Rate; + clock.IsRunning = parentClock.IsRunning; + + if (!isAttached) + { + clock.CurrentTime = parentClock.CurrentTime; + } + else + { + double? newTime = replayInputHandler.SetFrameFromTime(parentClock.CurrentTime); + + if (newTime == null) + { + // we shouldn't execute for this time value. probably waiting on more replay data. + validState = false; + return; + } + + clock.CurrentTime = newTime.Value; + } + + requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime; + base.Update(); + } + + protected override void TransformState(InputState state) + { + base.TransformState(state); + + // we don't want to transform the state if a replay is present (for now, at least). + if (replayInputHandler != null) return; + + var mouse = state.Mouse as Framework.Input.MouseState; + + if (mouse != null) + { + if (mouseDisabled.Value) + { + mouse.SetPressed(MouseButton.Left, false); + mouse.SetPressed(MouseButton.Right, false); + } + } + } + public void Attach(KeyCounterCollection keyCounter) { var receptor = new ActionReceptor(keyCounter); @@ -37,6 +193,11 @@ namespace osu.Game.Rulesets.UI } } + public interface IHasReplayHandler + { + ReplayInputHandler ReplayInputHandler { get; set; } + } + public interface ICanAttachKeyCounter { void Attach(KeyCounterCollection keyCounter); diff --git a/osu.Game/Screens/Play/PlayerInputManager.cs b/osu.Game/Screens/Play/PlayerInputManager.cs deleted file mode 100644 index f5e57f9e9d..0000000000 --- a/osu.Game/Screens/Play/PlayerInputManager.cs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using OpenTK.Input; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Input; -using osu.Framework.Timing; -using osu.Game.Configuration; -using osu.Game.Input.Handlers; - -namespace osu.Game.Screens.Play -{ - public class PlayerInputManager : PassThroughInputManager - { - private ManualClock clock; - private IFrameBasedClock parentClock; - - private ReplayInputHandler replayInputHandler; - public ReplayInputHandler ReplayInputHandler - { - get - { - return replayInputHandler; - } - set - { - if (replayInputHandler != null) RemoveHandler(replayInputHandler); - - replayInputHandler = value; - UseParentState = replayInputHandler == null; - - if (replayInputHandler != null) - AddHandler(replayInputHandler); - } - } - - private Bindable mouseDisabled; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - //our clock will now be our parent's clock, but we want to replace this to allow manual control. - parentClock = Clock; - - Clock = new FramedClock(clock = new ManualClock - { - CurrentTime = parentClock.CurrentTime, - Rate = parentClock.Rate, - }); - } - - /// - /// Whether we running up-to-date with our parent clock. - /// If not, we will need to keep processing children until we catch up. - /// - private bool requireMoreUpdateLoops; - - /// - /// Whether we in a valid state (ie. should we keep processing children frames). - /// This should be set to false when the replay is, for instance, waiting for future frames to arrive. - /// - private bool validState; - - protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState; - - private bool isAttached => replayInputHandler != null && !UseParentState; - - private const int max_catch_up_updates_per_frame = 50; - - public override bool UpdateSubTree() - { - requireMoreUpdateLoops = true; - validState = true; - - int loops = 0; - - while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame) - if (!base.UpdateSubTree()) - return false; - - return true; - } - - protected override void Update() - { - if (parentClock == null) return; - - clock.Rate = parentClock.Rate; - clock.IsRunning = parentClock.IsRunning; - - if (!isAttached) - { - clock.CurrentTime = parentClock.CurrentTime; - } - else - { - double? newTime = replayInputHandler.SetFrameFromTime(parentClock.CurrentTime); - - if (newTime == null) - { - // we shouldn't execute for this time value. probably waiting on more replay data. - validState = false; - return; - } - - clock.CurrentTime = newTime.Value; - } - - requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime; - base.Update(); - } - - protected override void TransformState(InputState state) - { - base.TransformState(state); - - // we don't want to transform the state if a replay is present (for now, at least). - if (replayInputHandler != null) return; - - var mouse = state.Mouse as Framework.Input.MouseState; - - if (mouse != null) - { - if (mouseDisabled.Value) - { - mouse.SetPressed(MouseButton.Left, false); - mouse.SetPressed(MouseButton.Right, false); - } - } - } - } -} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 32a8df6357..4e9e769cb7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -321,7 +321,6 @@ - From a7a7e0323f8ff199d10b3ccb11d6c3c9539348b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 15:36:42 +0900 Subject: [PATCH 45/71] Update autoplay and replay handling to result in actions, not keys --- .../Replays/OsuReplayInputHandler.cs | 32 +++++++++++++++++++ .../UI/OsuRulesetContainer.cs | 4 +++ .../osu.Game.Rulesets.Osu.csproj | 1 + .../Replays/TaikoFramedReplayInputHandler.cs | 16 ++++------ osu.Game/Input/Handlers/ReplayInputHandler.cs | 15 +++++++++ .../Replays/FramedReplayInputHandler.cs | 28 +++------------- osu.Game/Rulesets/UI/RulesetContainer.cs | 2 +- 7 files changed, 64 insertions(+), 34 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs new file mode 100644 index 0000000000..a54679b8cc --- /dev/null +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using osu.Framework.Input; +using osu.Game.Rulesets.Replays; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Replays +{ + public class OsuReplayInputHandler : FramedReplayInputHandler + { + public OsuReplayInputHandler(Replay replay) + : base(replay) + { + } + + public override List GetPendingStates() + { + List actions = new List(); + + if (CurrentFrame?.MouseLeft ?? false) actions.Add(OsuAction.LeftButton); + if (CurrentFrame?.MouseRight ?? false) actions.Add(OsuAction.RightButton); + + return new List + { + new ReplayState + { + Mouse = new ReplayMouseState(ToScreenSpace(Position ?? Vector2.Zero)), + PressedActions = actions + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs index 083f11945c..0b87bf7346 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs @@ -10,9 +10,11 @@ using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Osu.UI { @@ -49,6 +51,8 @@ namespace osu.Game.Rulesets.Osu.UI return null; } + protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay); + protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f); } } diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 1422ded407..0c9e53cf69 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -78,6 +78,7 @@ + diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs index f6425dd66f..89093ae05e 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs @@ -4,7 +4,6 @@ using osu.Game.Rulesets.Replays; using System.Collections.Generic; using osu.Framework.Input; -using OpenTK.Input; namespace osu.Game.Rulesets.Taiko.Replays { @@ -17,21 +16,18 @@ namespace osu.Game.Rulesets.Taiko.Replays public override List GetPendingStates() { - var keys = new List(); + var keys = new List(); if (CurrentFrame?.MouseRight1 == true) - keys.Add(Key.F); + keys.Add(TaikoAction.LeftCentre); if (CurrentFrame?.MouseRight2 == true) - keys.Add(Key.J); + keys.Add(TaikoAction.RightCentre); if (CurrentFrame?.MouseLeft1 == true) - keys.Add(Key.D); + keys.Add(TaikoAction.LeftRim); if (CurrentFrame?.MouseLeft2 == true) - keys.Add(Key.K); + keys.Add(TaikoAction.RightRim); - return new List - { - new InputState { Keyboard = new ReplayKeyboardState(keys) } - }; + return new List { new ReplayState { PressedActions = keys } }; } } } diff --git a/osu.Game/Input/Handlers/ReplayInputHandler.cs b/osu.Game/Input/Handlers/ReplayInputHandler.cs index 76e038048c..5f86495580 100644 --- a/osu.Game/Input/Handlers/ReplayInputHandler.cs +++ b/osu.Game/Input/Handlers/ReplayInputHandler.cs @@ -2,6 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; +using osu.Framework.Input; using osu.Framework.Input.Handlers; using osu.Framework.Platform; using OpenTK; @@ -29,5 +31,18 @@ namespace osu.Game.Input.Handlers public override bool IsActive => true; public override int Priority => 0; + + public class ReplayState : InputState + where T : struct + { + public List PressedActions; + + public override InputState Clone() + { + var clone = (ReplayState)base.Clone(); + clone.PressedActions = new List(PressedActions); + return clone; + } + } } } \ No newline at end of file diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 60da35fd91..f0d68c0467 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Input; using osu.Framework.MathUtils; using osu.Game.Input.Handlers; @@ -18,7 +17,7 @@ namespace osu.Game.Rulesets.Replays /// The ReplayHandler will take a replay and handle the propagation of updates to the input stack. /// It handles logic of any frames which *must* be executed. /// - public class FramedReplayInputHandler : ReplayInputHandler + public abstract class FramedReplayInputHandler : ReplayInputHandler { private readonly Replay replay; @@ -31,7 +30,7 @@ namespace osu.Game.Rulesets.Replays private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, Frames.Count - 1); - public FramedReplayInputHandler(Replay replay) + protected FramedReplayInputHandler(Replay replay) { this.replay = replay; } @@ -51,7 +50,7 @@ namespace osu.Game.Rulesets.Replays { } - private Vector2? position + protected Vector2? Position { get { @@ -62,23 +61,7 @@ namespace osu.Game.Rulesets.Replays } } - public override List GetPendingStates() - { - var buttons = new HashSet(); - if (CurrentFrame?.MouseLeft ?? false) - buttons.Add(MouseButton.Left); - if (CurrentFrame?.MouseRight ?? false) - buttons.Add(MouseButton.Right); - - return new List - { - new InputState - { - Mouse = new ReplayMouseState(ToScreenSpace(position ?? Vector2.Zero), buttons), - Keyboard = new ReplayKeyboardState(new List()) - } - }; - } + public override List GetPendingStates() => new List(); public bool AtLastFrame => currentFrameIndex == Frames.Count - 1; public bool AtFirstFrame => currentFrameIndex == 0; @@ -133,10 +116,9 @@ namespace osu.Game.Rulesets.Replays protected class ReplayMouseState : MouseState { - public ReplayMouseState(Vector2 position, IEnumerable list) + public ReplayMouseState(Vector2 position) { Position = position; - list.ForEach(b => SetPressed(b, true)); } } diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 0a16335c77..17417856a1 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.UI /// The input manager. public abstract PassThroughInputManager CreateInputManager(); - protected virtual FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new FramedReplayInputHandler(replay); + protected virtual FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => null; public Replay Replay { get; private set; } From f0635af40deeae8f3c44bcd328a9a39a6c9c7583 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 15:48:40 +0900 Subject: [PATCH 46/71] Add documentation and regions to RulesetInputManager --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 43 +++++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index bd7e51e937..75e273adea 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -23,6 +23,8 @@ namespace osu.Game.Rulesets.UI { } + #region Action mapping (for replays) + private List lastPressedActions = new List(); protected override void HandleNewState(InputState state) @@ -47,8 +49,9 @@ namespace osu.Game.Rulesets.UI lastPressedActions = newActions; } - private ManualClock clock; - private IFrameBasedClock parentClock; + #endregion + + #region IHasReplayHandler private ReplayInputHandler replayInputHandler; public ReplayInputHandler ReplayInputHandler @@ -69,13 +72,12 @@ namespace osu.Game.Rulesets.UI } } - private Bindable mouseDisabled; + #endregion - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); - } + #region Clock control + + private ManualClock clock; + private IFrameBasedClock parentClock; protected override void LoadComplete() { @@ -152,6 +154,18 @@ namespace osu.Game.Rulesets.UI base.Update(); } + #endregion + + #region Setting application (disables etc.) + + private Bindable mouseDisabled; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); + } + protected override void TransformState(InputState state) { base.TransformState(state); @@ -171,6 +185,10 @@ namespace osu.Game.Rulesets.UI } } + #endregion + + #region Key Counter Attachment + public void Attach(KeyCounterCollection keyCounter) { var receptor = new ActionReceptor(keyCounter); @@ -191,13 +209,22 @@ namespace osu.Game.Rulesets.UI public bool OnReleased(T action) => Target.Children.OfType>().Any(c => c.OnReleased(action)); } + + #endregion } + /// + /// Expose the in a capable . + /// public interface IHasReplayHandler { ReplayInputHandler ReplayInputHandler { get; set; } } + /// + /// Supports attaching a . + /// Keys will be populated automatically and a receptor will be injected inside. + /// public interface ICanAttachKeyCounter { void Attach(KeyCounterCollection keyCounter); From 76a95495d36e8069159775cc82a008317c47abc0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 17:30:10 +0900 Subject: [PATCH 47/71] Move shared code to base class --- osu.Game/Overlays/Direct/DirectGridPanel.cs | 11 ----------- osu.Game/Overlays/Direct/DirectListPanel.cs | 15 --------------- osu.Game/Overlays/Direct/DirectPanel.cs | 17 +++++++++++++++++ 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index 98ab5e88f8..822edd02df 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -26,22 +26,11 @@ namespace osu.Game.Overlays.Direct { Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image) CornerRadius = 4; - Masking = true; - - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 1f), - Radius = 3f, - Colour = Color4.Black.Opacity(0.25f), - }; } protected override void LoadComplete() { base.LoadComplete(); - - this.FadeInFromZero(200, Easing.Out); bottomPanel.LayoutDuration = 200; bottomPanel.LayoutEasing = Easing.Out; bottomPanel.Origin = Anchor.BottomLeft; diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 5b45fc7725..0821fbea3a 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -29,21 +29,6 @@ namespace osu.Game.Overlays.Direct RelativeSizeAxes = Axes.X; Height = height; CornerRadius = 5; - Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 1f), - Radius = 3f, - Colour = Color4.Black.Opacity(0.25f), - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - this.FadeInFromZero(200, Easing.Out); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 2f048b0e3d..9395e6817d 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Framework.Extensions.Color4Extensions; using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -10,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using OpenTK.Graphics; namespace osu.Game.Overlays.Direct { @@ -20,6 +22,21 @@ namespace osu.Game.Overlays.Direct protected DirectPanel(BeatmapSetInfo setInfo) { SetInfo = setInfo; + + Masking = true; + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 3f, + Colour = Color4.Black.Opacity(0.25f), + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + this.FadeInFromZero(200, Easing.Out); } protected List GetDifficultyIcons() From 4e1cf329c88628cfaa537dd81c420c3350534e6d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 17:39:39 +0900 Subject: [PATCH 48/71] Move background logic to base class; reduce overdraw after set fades in --- osu.Game/Overlays/Direct/DirectGridPanel.cs | 10 ++------- osu.Game/Overlays/Direct/DirectPanel.cs | 25 ++++++++++++++++++++- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index 822edd02df..5b648633c1 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -39,14 +39,8 @@ namespace osu.Game.Overlays.Direct [BackgroundDependencyLoader] private void load(OsuColour colours, LocalisationEngine localisation) { - Children = new[] + AddRange(new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - CreateBackground(), new Box { RelativeSizeAxes = Axes.Both, @@ -174,7 +168,7 @@ namespace osu.Game.Overlays.Direct new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0), }, }, - }; + }); } } } diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 9395e6817d..a56c15b532 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -2,10 +2,12 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; @@ -19,6 +21,8 @@ namespace osu.Game.Overlays.Direct { protected readonly BeatmapSetInfo SetInfo; + protected Box BlackBackground; + protected DirectPanel(BeatmapSetInfo setInfo) { SetInfo = setInfo; @@ -33,6 +37,21 @@ namespace osu.Game.Overlays.Direct }; } + [BackgroundDependencyLoader] + private void load() + { + AddRange(new[] + { + // temporary blackness until the actual background loads. + BlackBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + CreateBackground(), + }); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -55,7 +74,11 @@ namespace osu.Game.Overlays.Direct Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fill, - OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out), + OnLoadComplete = d => + { + d.FadeInFromZero(400, Easing.Out); + BlackBackground.Delay(400).FadeOut(); + }, }) { RelativeSizeAxes = Axes.Both, From a2549157ca578f548a4342e7f6d7a3ba0ffcbf90 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 18:18:03 +0900 Subject: [PATCH 49/71] Add hover effects --- osu.Game/Overlays/Direct/DirectGridPanel.cs | 3 +- osu.Game/Overlays/Direct/DirectListPanel.cs | 13 +--- osu.Game/Overlays/Direct/DirectPanel.cs | 85 +++++++++++++++++---- 3 files changed, 76 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index 5b648633c1..0cc1c18d8d 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -25,7 +25,6 @@ namespace osu.Game.Overlays.Direct public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap) { Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image) - CornerRadius = 4; } protected override void LoadComplete() @@ -39,6 +38,8 @@ namespace osu.Game.Overlays.Direct [BackgroundDependencyLoader] private void load(OsuColour colours, LocalisationEngine localisation) { + Content.CornerRadius = 4; + AddRange(new Drawable[] { new Box diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 0821fbea3a..d284c50bc4 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -28,20 +28,15 @@ namespace osu.Game.Overlays.Direct { RelativeSizeAxes = Axes.X; Height = height; - CornerRadius = 5; } [BackgroundDependencyLoader] private void load(LocalisationEngine localisation) { - Children = new[] + Content.CornerRadius = 5; + + AddRange(new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - CreateBackground(), new Box { RelativeSizeAxes = Axes.Both, @@ -132,7 +127,7 @@ namespace osu.Game.Overlays.Direct }, }, }, - }; + }); } private class DownloadButton : OsuClickableContainer diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index a56c15b532..7e95c1674b 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -9,11 +9,14 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using OpenTK.Graphics; +using osu.Framework.Input; +using osu.Framework.MathUtils; namespace osu.Game.Overlays.Direct { @@ -23,35 +26,62 @@ namespace osu.Game.Overlays.Direct protected Box BlackBackground; + private const double hover_transition_time = 400; + + private Container content; + + protected override Container Content => content; + protected DirectPanel(BeatmapSetInfo setInfo) { SetInfo = setInfo; - - Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 1f), - Radius = 3f, - Colour = Color4.Black.Opacity(0.25f), - }; } [BackgroundDependencyLoader] private void load() { - AddRange(new[] + AddInternal(content = new Container { - // temporary blackness until the actual background loads. - BlackBackground = new Box + RelativeSizeAxes = Axes.Both, + Masking = true, + EdgeEffect = new EdgeEffectParameters { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 2f, + Colour = Color4.Black.Opacity(0.25f), }, - CreateBackground(), + Children = new[] + { + // temporary blackness until the actual background loads. + BlackBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + CreateBackground(), + } }); } + protected override bool OnHover(InputState state) + { + content.FadeEdgeEffectTo(1f, hover_transition_time, Easing.OutQuint); + content.TransformTo(content.PopulateTransform(new TransformEdgeEffectRadius(), 14, hover_transition_time, Easing.OutQuint)); + content.MoveToY(-4, hover_transition_time, Easing.OutQuint); + + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + content.FadeEdgeEffectTo(0.25f, hover_transition_time, Easing.OutQuint); + content.TransformTo(content.PopulateTransform(new TransformEdgeEffectRadius(), 2, hover_transition_time, Easing.OutQuint)); + content.MoveToY(0, hover_transition_time, Easing.OutQuint); + + base.OnHoverLost(state); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -127,5 +157,30 @@ namespace osu.Game.Overlays.Direct Value = value; } } + + private class TransformEdgeEffectRadius : Transform + { + /// + /// Current value of the transformed colour in linear colour space. + /// + private float valueAt(double time) + { + if (time < StartTime) return StartValue; + if (time >= EndTime) return EndValue; + + return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing); + } + + public override string TargetMember => "EdgeEffect.Colour"; + + protected override void Apply(Container c, double time) + { + EdgeEffectParameters e = c.EdgeEffect; + e.Radius = valueAt(time); + c.EdgeEffect = e; + } + + protected override void ReadIntoStartValue(Container d) => StartValue = d.EdgeEffect.Radius; + } } } From 36629f52074162d881c245d2293f139e889b39da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 18:51:34 +0900 Subject: [PATCH 50/71] Make ProgressBar usable in more places than just MusicController --- .../Graphics/UserInterface/ProgressBar.cs | 67 +++++++++++++++++++ osu.Game/Overlays/MusicController.cs | 45 ------------- osu.Game/osu.Game.csproj | 1 + 3 files changed, 68 insertions(+), 45 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/ProgressBar.cs diff --git a/osu.Game/Graphics/UserInterface/ProgressBar.cs b/osu.Game/Graphics/UserInterface/ProgressBar.cs new file mode 100644 index 0000000000..f22983a6e6 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ProgressBar.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 System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using OpenTK.Graphics; + +namespace osu.Game.Graphics.UserInterface +{ + public class ProgressBar : SliderBar + { + public Action OnSeek; + + private readonly Box fill; + private readonly Box background; + + public Color4 FillColour + { + set { fill.Colour = value; } + } + + public Color4 BackgroundColour + { + set + { + background.Alpha = 1; + background.Colour = value; + } + } + + public double EndTime + { + set { CurrentNumber.MaxValue = value; } + } + + public double CurrentTime + { + set { CurrentNumber.Value = value; } + } + + public ProgressBar() + { + CurrentNumber.MinValue = 0; + CurrentNumber.MaxValue = 1; + RelativeSizeAxes = Axes.X; + + Children = new Drawable[] + { + background = new Box + { + Alpha = 0, + RelativeSizeAxes = Axes.Both + }, + fill = new Box { RelativeSizeAxes = Axes.Y } + }; + } + + protected override void UpdateValue(float value) + { + fill.Width = value * UsableWidth; + } + + protected override void OnUserChange() => OnSeek?.Invoke(Current); + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index d970089942..f2c90f009a 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -15,7 +15,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; using osu.Framework.Localisation; using osu.Framework.Threading; @@ -434,49 +433,5 @@ namespace osu.Game.Overlays sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4"); } } - - private class ProgressBar : SliderBar - { - public Action OnSeek; - - private readonly Box fill; - - public Color4 FillColour - { - set { fill.Colour = value; } - } - - public double EndTime - { - set { CurrentNumber.MaxValue = value; } - } - - public double CurrentTime - { - set { CurrentNumber.Value = value; } - } - - public ProgressBar() - { - CurrentNumber.MinValue = 0; - CurrentNumber.MaxValue = 1; - RelativeSizeAxes = Axes.X; - - Children = new Drawable[] - { - fill = new Box - { - RelativeSizeAxes = Axes.Y - } - }; - } - - protected override void UpdateValue(float value) - { - fill.Width = value * UsableWidth; - } - - protected override void OnUserChange() => OnSeek?.Invoke(Current); - } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 32a8df6357..f361c141f1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -116,6 +116,7 @@ + From cacf256aade865ecc9748219827b56a06510125e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 18:51:50 +0900 Subject: [PATCH 51/71] Add placeholder download method with progress bar --- osu.Game/Overlays/Direct/DirectGridPanel.cs | 7 +++++ osu.Game/Overlays/Direct/DirectListPanel.cs | 1 + osu.Game/Overlays/Direct/DirectPanel.cs | 29 ++++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index 0cc1c18d8d..3a9e75bd38 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; +using osu.Framework.Input; namespace osu.Game.Overlays.Direct { @@ -171,5 +172,11 @@ namespace osu.Game.Overlays.Direct }, }); } + + protected override bool OnClick(InputState state) + { + StartDownload(); + return true; + } } } diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index d284c50bc4..b3502b0827 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -124,6 +124,7 @@ namespace osu.Game.Overlays.Direct Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Size = new Vector2(height - vertical_padding * 2), + Action = StartDownload }, }, }, diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 7e95c1674b..8738d8ea90 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -17,6 +17,8 @@ using osu.Game.Graphics.Sprites; using OpenTK.Graphics; using osu.Framework.Input; using osu.Framework.MathUtils; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; namespace osu.Game.Overlays.Direct { @@ -30,6 +32,9 @@ namespace osu.Game.Overlays.Direct private Container content; + private APIAccess api; + private ProgressBar progressBar; + protected override Container Content => content; protected DirectPanel(BeatmapSetInfo setInfo) @@ -38,8 +43,10 @@ namespace osu.Game.Overlays.Direct } [BackgroundDependencyLoader] - private void load() + private void load(APIAccess api, OsuColour colours) { + this.api = api; + AddInternal(content = new Container { RelativeSizeAxes = Axes.Both, @@ -60,6 +67,16 @@ namespace osu.Game.Overlays.Direct Colour = Color4.Black, }, CreateBackground(), + progressBar = new ProgressBar + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Height = 0, + Alpha = 0, + BackgroundColour = Color4.Black.Opacity(0.7f), + FillColour = colours.Blue, + Depth = -1, + }, } }); } @@ -82,6 +99,16 @@ namespace osu.Game.Overlays.Direct base.OnHoverLost(state); } + protected void StartDownload() + { + progressBar.FadeIn(400, Easing.OutQuint); + progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); + + progressBar.Current.Value = 0.5f; + + api.Queue(new APIRequest()); + } + protected override void LoadComplete() { base.LoadComplete(); From dac54c362a4050107ac11135ecf1445aff8af809 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 15:39:35 +0900 Subject: [PATCH 52/71] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 1ba1e8ef1e..da5fbf8c58 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 1ba1e8ef1e5ec0466632be02492023a081cb85ab +Subproject commit da5fbf8c580b079671298f6da54be10a00bf434c From 314108146a9ae75bcdb82b7413bd998794febc94 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 20:14:17 +0900 Subject: [PATCH 53/71] Add a download API request --- osu.Game/Online/API/APIRequest.cs | 43 +++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 23268a9ca9..307afb2d2b 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -11,11 +11,11 @@ namespace osu.Game.Online.API /// An API request with a well-defined response type. /// /// Type of the response (used for deserialisation). - public class APIRequest : APIRequest + public abstract class APIRequest : APIRequest { protected override WebRequest CreateWebRequest() => new JsonWebRequest(Uri); - public APIRequest() + protected APIRequest() { base.Success += onSuccess; } @@ -28,10 +28,36 @@ namespace osu.Game.Online.API public new event APISuccessHandler Success; } + public abstract class APIDownloadRequest : APIRequest + { + protected override WebRequest CreateWebRequest() + { + var request = new WebRequest(Uri); + request.DownloadProgress += request_Progress; + return request; + } + + private void request_Progress(WebRequest request, long current, long total) => API.Scheduler.Add(delegate { Progress?.Invoke(current, total); }); + + protected APIDownloadRequest() + { + base.Success += onSuccess; + } + + private void onSuccess() + { + Success?.Invoke(WebRequest.ResponseData); + } + + public event APIProgressHandler Progress; + + public new event APISuccessHandler Success; + } + /// /// AN API request with no specified response type. /// - public class APIRequest + public abstract class APIRequest { /// /// The maximum amount of time before this request will fail. @@ -42,7 +68,7 @@ namespace osu.Game.Online.API protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri); - protected virtual string Uri => $@"{api.Endpoint}/api/v2/{Target}"; + protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}"; private double remainingTime => Math.Max(0, Timeout - (DateTime.Now.TotalMilliseconds() - (startTime ?? 0))); @@ -52,7 +78,7 @@ namespace osu.Game.Online.API public double StartTime => startTime ?? -1; - private APIAccess api; + protected APIAccess API; protected WebRequest WebRequest; public event APISuccessHandler Success; @@ -64,7 +90,7 @@ namespace osu.Game.Online.API public void Perform(APIAccess api) { - this.api = api; + API = api; if (checkAndProcessFailure()) return; @@ -109,9 +135,9 @@ namespace osu.Game.Online.API /// Whether we are in a failed or cancelled state. private bool checkAndProcessFailure() { - if (api == null || pendingFailure == null) return cancelled; + if (API == null || pendingFailure == null) return cancelled; - api.Scheduler.Add(pendingFailure); + API.Scheduler.Add(pendingFailure); pendingFailure = null; return true; } @@ -119,5 +145,6 @@ namespace osu.Game.Online.API public delegate void APIFailureHandler(Exception e); public delegate void APISuccessHandler(); + public delegate void APIProgressHandler(long current, long total); public delegate void APISuccessHandler(T content); } From 3c10b2d3d9b44e06bf95f88a8c26371d976634b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 20:14:35 +0900 Subject: [PATCH 54/71] Populate set IDs in GetBeatmapSetsResponse --- osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs index ca984d3511..e4763f73ee 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs @@ -46,6 +46,9 @@ namespace osu.Game.Online.API.Requests [JsonProperty(@"favourite_count")] private int favouriteCount { get; set; } + [JsonProperty(@"id")] + private int onlineId { get; set; } + [JsonProperty(@"beatmaps")] private IEnumerable beatmaps { get; set; } @@ -53,6 +56,7 @@ namespace osu.Game.Online.API.Requests { return new BeatmapSetInfo { + OnlineBeatmapSetID = onlineId, Metadata = this, OnlineInfo = new BeatmapSetOnlineInfo { From 9c82593c9e82a23ebc9f435674f1b6172a1351e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 20:15:45 +0900 Subject: [PATCH 55/71] Add cancel event to ProgressNotification --- osu.Game/Overlays/Notifications/ProgressNotification.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index f42b4b6cb3..a31291e1b8 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -152,11 +152,14 @@ namespace osu.Game.Overlays.Notifications break; case ProgressNotificationState.Active: case ProgressNotificationState.Queued: - State = ProgressNotificationState.Cancelled; + if (CancelRequested?.Invoke() != false) + State = ProgressNotificationState.Cancelled; break; } } + public Func CancelRequested { get; set; } + /// /// The function to post completion notifications back to. /// From 32a23c7fe41fd2c4f978385fdb6f32f576d9e0dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 20:16:03 +0900 Subject: [PATCH 56/71] Add initial osu!direct beatmap download and import process --- osu.Game/Overlays/Direct/DirectPanel.cs | 82 ++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 8738d8ea90..b44a100d3c 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; +using System.IO; +using System.Threading.Tasks; using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -19,6 +21,9 @@ using osu.Framework.Input; using osu.Framework.MathUtils; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; +using osu.Framework.Logging; +using osu.Game.Beatmaps.IO; +using osu.Game.Overlays.Notifications; namespace osu.Game.Overlays.Direct { @@ -34,6 +39,8 @@ namespace osu.Game.Overlays.Direct private APIAccess api; private ProgressBar progressBar; + private BeatmapManager beatmaps; + private NotificationOverlay notifications; protected override Container Content => content; @@ -43,9 +50,11 @@ namespace osu.Game.Overlays.Direct } [BackgroundDependencyLoader] - private void load(APIAccess api, OsuColour colours) + private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications) { this.api = api; + this.beatmaps = beatmaps; + this.notifications = notifications; AddInternal(content = new Container { @@ -101,12 +110,79 @@ namespace osu.Game.Overlays.Direct protected void StartDownload() { + if (!api.LocalUser.Value.IsSupporter) + { + notifications.Post(new SimpleNotification + { + Icon = FontAwesome.fa_superpowers, + Text = "You gotta be a supporter to download for now 'yo" + }); + return; + } + progressBar.FadeIn(400, Easing.OutQuint); progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); - progressBar.Current.Value = 0.5f; + progressBar.Current.Value = 0; - api.Queue(new APIRequest()); + ProgressNotification downloadNotification = new ProgressNotification + { + Text = $"Downloading {SetInfo.Metadata.Artist} - {SetInfo.Metadata.Title}", + }; + + var request = new DownloadBeatmapSetRequest(SetInfo); + request.Failure += e => + { + progressBar.Current.Value = 0; + progressBar.FadeOut(500); + downloadNotification.State = ProgressNotificationState.Completed; + Logger.Error(e, "Failed to get beatmap download information"); + }; + + request.Progress += (current, total) => + { + float progress = (float)current / total; + + progressBar.Current.Value = progress; + + downloadNotification.State = ProgressNotificationState.Active; + downloadNotification.Progress = progress; + }; + + request.Success += data => + { + progressBar.Current.Value = 1; + progressBar.FadeOut(500); + + downloadNotification.State = ProgressNotificationState.Completed; + + using (var stream = new MemoryStream(data)) + using (var archive = new OszArchiveReader(stream)) + beatmaps.Import(archive); + }; + + downloadNotification.CancelRequested += () => + { + request.Cancel(); + return true; + }; + + notifications.Post(downloadNotification); + + // don't run in the main api queue as this is a long-running task. + Task.Run(() => request.Perform(api)); + } + + public class DownloadBeatmapSetRequest : APIDownloadRequest + { + private readonly BeatmapSetInfo beatmapSet; + + public DownloadBeatmapSetRequest(BeatmapSetInfo beatmapSet) + { + this.beatmapSet = beatmapSet; + } + + protected override string Target => $@"beatmapsets/{beatmapSet.OnlineBeatmapSetID}/download"; } protected override void LoadComplete() From 9adff5f697221b0d7f8ab9460315c681345a6a3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 20:18:47 +0900 Subject: [PATCH 57/71] Add osu!direct toggle to toolbar --- osu.Game/OsuGame.cs | 1 + osu.Game/Overlays/Toolbar/Toolbar.cs | 1 + .../Overlays/Toolbar/ToolbarDirectButton.cs | 19 +++++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 4 files changed, 22 insertions(+) create mode 100644 osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8c82071c23..43e3731166 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -219,6 +219,7 @@ namespace osu.Game dependencies.Cache(settings); dependencies.Cache(social); + dependencies.Cache(direct); dependencies.Cache(chat); dependencies.Cache(userProfile); dependencies.Cache(musicController); diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 32344ca7eb..e36db1f9da 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -58,6 +58,7 @@ namespace osu.Game.Overlays.Toolbar AutoSizeAxes = Axes.X, Children = new Drawable[] { + new ToolbarDirectButton(), new ToolbarChatButton(), new ToolbarSocialButton(), new ToolbarMusicButton(), diff --git a/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs b/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs new file mode 100644 index 0000000000..484b079d23 --- /dev/null +++ b/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs @@ -0,0 +1,19 @@ +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + internal class ToolbarDirectButton : ToolbarOverlayToggleButton + { + public ToolbarDirectButton() + { + SetIcon(FontAwesome.fa_download); + } + + [BackgroundDependencyLoader] + private void load(DirectOverlay direct) + { + StateContainer = direct; + } + } +} \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f361c141f1..15c400b21e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -130,6 +130,7 @@ + From 0082640548d3e6cab5cdfbfe0ed2cb442511ccda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 20:25:18 +0900 Subject: [PATCH 58/71] Add missing licence header --- osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs b/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs index 484b079d23..32e149b31a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs @@ -1,3 +1,6 @@ +// 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.Game.Graphics; From a7e6efd34fc83ceb33cf7c8a31200d3d709e1dad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 20:30:18 +0900 Subject: [PATCH 59/71] Rename keys -> actions --- .../Replays/TaikoFramedReplayInputHandler.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs index 89093ae05e..fce0179721 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs @@ -16,18 +16,18 @@ namespace osu.Game.Rulesets.Taiko.Replays public override List GetPendingStates() { - var keys = new List(); + var actions = new List(); if (CurrentFrame?.MouseRight1 == true) - keys.Add(TaikoAction.LeftCentre); + actions.Add(TaikoAction.LeftCentre); if (CurrentFrame?.MouseRight2 == true) - keys.Add(TaikoAction.RightCentre); + actions.Add(TaikoAction.RightCentre); if (CurrentFrame?.MouseLeft1 == true) - keys.Add(TaikoAction.LeftRim); + actions.Add(TaikoAction.LeftRim); if (CurrentFrame?.MouseLeft2 == true) - keys.Add(TaikoAction.RightRim); + actions.Add(TaikoAction.RightRim); - return new List { new ReplayState { PressedActions = keys } }; + return new List { new ReplayState { PressedActions = actions } }; } } } From c9f90efb8ac418d52949628a0018f31bf605b0a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 20:31:57 +0900 Subject: [PATCH 60/71] Add more checks and remove direct cast --- osu.Game/Rulesets/UI/RulesetContainer.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 17417856a1..a7472f4dbc 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.UI /// /// The input manager for this RulesetContainer. /// - internal IHasReplayHandler ReplayInputManager => (IHasReplayHandler)KeyBindingInputManager; + internal IHasReplayHandler ReplayInputManager => KeyBindingInputManager as IHasReplayHandler; /// /// The key conversion input manager for this RulesetContainer. @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.UI /// /// Whether we have a replay loaded currently. /// - public bool HasReplayLoaded => ReplayInputManager.ReplayInputHandler != null; + public bool HasReplayLoaded => ReplayInputManager?.ReplayInputHandler != null; public abstract IEnumerable Objects { get; } @@ -107,6 +107,9 @@ namespace osu.Game.Rulesets.UI /// The replay, null for local input. public virtual void SetReplay(Replay replay) { + if (ReplayInputManager == null) + throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports replay loading is not available"); + Replay = replay; ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; } From 2b667cf789a57ac5c73a5c274dbceef2ba544086 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 20:32:55 +0900 Subject: [PATCH 61/71] Fix typos --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 75e273adea..0419070b42 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -94,13 +94,13 @@ namespace osu.Game.Rulesets.UI } /// - /// Whether we running up-to-date with our parent clock. + /// Whether we are running up-to-date with our parent clock. /// If not, we will need to keep processing children until we catch up. /// private bool requireMoreUpdateLoops; /// - /// Whether we in a valid state (ie. should we keep processing children frames). + /// Whether we are in a valid state (ie. should we keep processing children frames). /// This should be set to false when the replay is, for instance, waiting for future frames to arrive. /// private bool validState; @@ -229,4 +229,4 @@ namespace osu.Game.Rulesets.UI { void Attach(KeyCounterCollection keyCounter); } -} \ No newline at end of file +} From 1b0a1dd4108285dbec840ad8cf9a9ef1b014d066 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 20:37:03 +0900 Subject: [PATCH 62/71] Add missing licence header --- osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs index a54679b8cc..0811a68392 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// 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 osu.Framework.Input; using osu.Game.Rulesets.Replays; using OpenTK; From febf0348be646d1af5aeda1ae1ee8dcf5206a298 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2017 21:26:50 +0900 Subject: [PATCH 63/71] Permit nulls to allow test cases to run successfully --- osu.Game/Overlays/Direct/DirectPanel.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index b44a100d3c..bc30fb83f5 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Direct SetInfo = setInfo; } - [BackgroundDependencyLoader] + [BackgroundDependencyLoader(permitNulls: true)] private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications) { this.api = api; @@ -110,6 +110,8 @@ namespace osu.Game.Overlays.Direct protected void StartDownload() { + if (api == null) return; + if (!api.LocalUser.Value.IsSupporter) { notifications.Post(new SimpleNotification From 2dd3e513732d7c0cde6ecd5e0c8d0b3543d337ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Aug 2017 10:51:28 +0900 Subject: [PATCH 64/71] Ensure other full-screen overlays are closed when direct is open (and vice-versa) --- osu.Game/OsuGame.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 43e3731166..82177ab05e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -226,6 +226,23 @@ namespace osu.Game dependencies.Cache(notificationOverlay); dependencies.Cache(dialogOverlay); + // ensure only one of these overlays are open at once. + var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; + foreach (var overlay in singleDisplayOverlays) + { + overlay.StateChanged += (container, state) => + { + if (state == Visibility.Hidden) return; + + foreach (var c in singleDisplayOverlays) + { + if (c == container) continue; + c.State = Visibility.Hidden; + } + }; + } + + // ensure both overlays aren't presented at the same time chat.StateChanged += (container, state) => social.State = state == Visibility.Visible ? Visibility.Hidden : social.State; social.StateChanged += (container, state) => chat.State = state == Visibility.Visible ? Visibility.Hidden : chat.State; From 7f617e2c3659ab1dad686c6971e056be0e169884 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Aug 2017 11:53:41 +0900 Subject: [PATCH 65/71] Remove downloaded beatmap panels from osu!direct --- osu.Game/Overlays/DirectOverlay.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index b1c7dab778..f270aec43e 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -161,11 +161,19 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(OsuColour colours, APIAccess api, RulesetStore rulesets) + private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, BeatmapManager beatmaps) { this.api = api; this.rulesets = rulesets; resultCountsContainer.Colour = colours.Yellow; + + beatmaps.BeatmapSetAdded += setAdded; + } + + private void setAdded(BeatmapSetInfo set) + { + // if a new map was imported, we should remove it from search results (download completed etc.) + panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire(); } private void updateResultCounts() From ca0d1b79b2b772e2c789be0b78c2a0dd7d50aa33 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Aug 2017 11:54:18 +0900 Subject: [PATCH 66/71] Disallow multiple download requests for the same panel --- osu.Game/Overlays/Direct/DirectPanel.cs | 30 +++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index bc30fb83f5..4f02ce1bf6 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Direct { public abstract class DirectPanel : Container { - protected readonly BeatmapSetInfo SetInfo; + public readonly BeatmapSetInfo SetInfo; protected Box BlackBackground; @@ -108,10 +108,23 @@ namespace osu.Game.Overlays.Direct base.OnHoverLost(state); } + // this should eventually be moved to a more central place, like BeatmapManager. + private DownloadBeatmapSetRequest downloadRequest; + protected void StartDownload() { if (api == null) return; + // we already have an active download running. + if (downloadRequest != null) + { + content.MoveToX(-5, 50, Easing.OutSine).Then() + .MoveToX(5, 100, Easing.InOutSine).Then() + .MoveToX(-5, 100, Easing.InOutSine).Then() + .MoveToX(0, 50, Easing.InSine).Then(); + return; + } + if (!api.LocalUser.Value.IsSupporter) { notifications.Post(new SimpleNotification @@ -132,16 +145,18 @@ namespace osu.Game.Overlays.Direct Text = $"Downloading {SetInfo.Metadata.Artist} - {SetInfo.Metadata.Title}", }; - var request = new DownloadBeatmapSetRequest(SetInfo); - request.Failure += e => + downloadRequest = new DownloadBeatmapSetRequest(SetInfo); + downloadRequest.Failure += e => { progressBar.Current.Value = 0; progressBar.FadeOut(500); downloadNotification.State = ProgressNotificationState.Completed; Logger.Error(e, "Failed to get beatmap download information"); + + downloadRequest = null; }; - request.Progress += (current, total) => + downloadRequest.Progress += (current, total) => { float progress = (float)current / total; @@ -151,7 +166,7 @@ namespace osu.Game.Overlays.Direct downloadNotification.Progress = progress; }; - request.Success += data => + downloadRequest.Success += data => { progressBar.Current.Value = 1; progressBar.FadeOut(500); @@ -165,14 +180,15 @@ namespace osu.Game.Overlays.Direct downloadNotification.CancelRequested += () => { - request.Cancel(); + downloadRequest.Cancel(); + downloadRequest = null; return true; }; notifications.Post(downloadNotification); // don't run in the main api queue as this is a long-running task. - Task.Run(() => request.Perform(api)); + Task.Run(() => downloadRequest.Perform(api)); } public class DownloadBeatmapSetRequest : APIDownloadRequest From 7055cb581d71a9d7fe438f7ec9e6beeba26824e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Aug 2017 11:54:35 +0900 Subject: [PATCH 67/71] Load direct panels more asynchronously to avoid stutter --- osu.Game/Overlays/DirectOverlay.cs | 60 +++++++++++++++--------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index f270aec43e..f734e43826 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays private readonly FillFlowContainer resultCountsContainer; private readonly OsuSpriteText resultCountsText; - private readonly FillFlowContainer panels; + private FillFlowContainer panels; protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74"); protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71"); @@ -48,17 +48,6 @@ namespace osu.Game.Overlays if (beatmapSets?.Equals(value) ?? false) return; beatmapSets = value; - if (BeatmapSets == null) - { - foreach (var p in panels.Children) - { - p.FadeOut(200); - p.Expire(); - } - - return; - } - recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value); } } @@ -108,13 +97,6 @@ namespace osu.Game.Overlays }, } }, - panels = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(panel_padding), - Margin = new MarginPadding { Top = 10 }, - }, }; Filter.Search.Current.ValueChanged += text => { if (text != string.Empty) Header.Tabs.Current.Value = DirectTab.Search; }; @@ -193,18 +175,38 @@ namespace osu.Game.Overlays private void recreatePanels(PanelDisplayStyle displayStyle) { + if (panels != null) + { + panels.FadeOut(200); + panels.Expire(); + panels = null; + } + if (BeatmapSets == null) return; - panels.ChildrenEnumerable = BeatmapSets.Select(b => - { - switch (displayStyle) - { - case PanelDisplayStyle.Grid: - return new DirectGridPanel(b) { Width = 400 }; - default: - return new DirectListPanel(b); - } - }); + var newPanels = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(panel_padding), + Margin = new MarginPadding { Top = 10 }, + ChildrenEnumerable = BeatmapSets.Select(b => + { + switch (displayStyle) + { + case PanelDisplayStyle.Grid: + return new DirectGridPanel(b) { Width = 400 }; + default: + return new DirectListPanel(b); + } + }) + }; + + LoadComponentAsync(newPanels, p => + { + if (panels != null) ScrollFlow.Remove(panels); + ScrollFlow.Add(panels = newPanels); + }); } private GetBeatmapSetsRequest getSetsRequest; From 86bde4b6b253e87fd92ae8026878a4a5128f35cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Aug 2017 13:03:34 +0900 Subject: [PATCH 68/71] Use the correct icon for osu!direct in the toolbar --- osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs b/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs index 32e149b31a..dacb6d67b8 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs @@ -10,7 +10,7 @@ namespace osu.Game.Overlays.Toolbar { public ToolbarDirectButton() { - SetIcon(FontAwesome.fa_download); + SetIcon(FontAwesome.fa_osu_chevron_down_o); } [BackgroundDependencyLoader] From 67b3cbce2f9fa436d19f5ffa77982c83521a3442 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Aug 2017 13:04:32 +0900 Subject: [PATCH 69/71] Fix beatmap background being disposed too early Causes weird transitions on the music controller --- osu.Game/OsuGameBase.cs | 8 ++------ osu.Game/Overlays/MusicController.cs | 12 ++++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 952dc900a2..a7136ce803 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -152,14 +152,10 @@ namespace osu.Game Beatmap.ValueChanged += b => { - // compare to last baetmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) + // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) if (lastBeatmap?.Track != b.Track) { - // this disposal is done to stop the audio track. - // it may not be exactly what we want for cases beatmaps are reused, as it will - // trigger a fresh load of contained resources. - lastBeatmap?.Dispose(); - + lastBeatmap?.Track?.Dispose(); Audio.Track.AddItem(b.Track); } diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index f2c90f009a..cb4628825e 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -348,23 +348,23 @@ namespace osu.Game.Overlays playerContainer.Add(new AsyncLoadWrapper(new Background(beatmap) { - OnLoadComplete = d => + OnLoadComplete = newBackground => { switch (direction) { case TransformDirection.Next: - d.Position = new Vector2(400, 0); - d.MoveToX(0, 500, Easing.OutCubic); + newBackground.Position = new Vector2(400, 0); + newBackground.MoveToX(0, 500, Easing.OutCubic); currentBackground.MoveToX(-400, 500, Easing.OutCubic); break; case TransformDirection.Prev: - d.Position = new Vector2(-400, 0); - d.MoveToX(0, 500, Easing.OutCubic); + newBackground.Position = new Vector2(-400, 0); + newBackground.MoveToX(0, 500, Easing.OutCubic); currentBackground.MoveToX(400, 500, Easing.OutCubic); break; } currentBackground.Expire(); - currentBackground = d; + currentBackground = newBackground; } }) { From 72a16e31dd9468912df4c555d72ba10010e49adf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Aug 2017 13:07:10 +0900 Subject: [PATCH 70/71] Remove unnecessary old code --- osu.Game/OsuGame.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 82177ab05e..30bc09d50f 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -242,11 +242,6 @@ namespace osu.Game }; } - - // ensure both overlays aren't presented at the same time - chat.StateChanged += (container, state) => social.State = state == Visibility.Visible ? Visibility.Hidden : social.State; - social.StateChanged += (container, state) => chat.State = state == Visibility.Visible ? Visibility.Hidden : chat.State; - LoadComponentAsync(Toolbar = new Toolbar { Depth = -4, From 70154d10365fbf7929987778c65c15df104f30d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Aug 2017 14:36:14 +0900 Subject: [PATCH 71/71] Update usage of FadeEdgeEffect Improves the visual appearance of DirectPanels' shadows too. --- osu-framework | 2 +- osu.Game/Overlays/Direct/DirectPanel.cs | 58 +++++++------------ osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 4 +- 3 files changed, 23 insertions(+), 41 deletions(-) diff --git a/osu-framework b/osu-framework index da5fbf8c58..3db7e23165 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit da5fbf8c580b079671298f6da54be10a00bf434c +Subproject commit 3db7e231653ec6ffe28b5dcd1a86230ec754cc1c diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 4f02ce1bf6..a642f72821 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -11,14 +11,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Transforms; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using OpenTK.Graphics; using osu.Framework.Input; -using osu.Framework.MathUtils; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Framework.Logging; @@ -49,6 +47,23 @@ namespace osu.Game.Overlays.Direct SetInfo = setInfo; } + private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 2f, + Colour = Color4.Black.Opacity(0.25f), + }; + + private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 5f), + Radius = 10f, + Colour = Color4.Black.Opacity(0.3f), + }; + + [BackgroundDependencyLoader(permitNulls: true)] private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications) { @@ -60,13 +75,7 @@ namespace osu.Game.Overlays.Direct { RelativeSizeAxes = Axes.Both, Masking = true, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 1f), - Radius = 2f, - Colour = Color4.Black.Opacity(0.25f), - }, + EdgeEffect = edgeEffectNormal, Children = new[] { // temporary blackness until the actual background loads. @@ -92,8 +101,7 @@ namespace osu.Game.Overlays.Direct protected override bool OnHover(InputState state) { - content.FadeEdgeEffectTo(1f, hover_transition_time, Easing.OutQuint); - content.TransformTo(content.PopulateTransform(new TransformEdgeEffectRadius(), 14, hover_transition_time, Easing.OutQuint)); + content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint); content.MoveToY(-4, hover_transition_time, Easing.OutQuint); return base.OnHover(state); @@ -101,8 +109,7 @@ namespace osu.Game.Overlays.Direct protected override void OnHoverLost(InputState state) { - content.FadeEdgeEffectTo(0.25f, hover_transition_time, Easing.OutQuint); - content.TransformTo(content.PopulateTransform(new TransformEdgeEffectRadius(), 2, hover_transition_time, Easing.OutQuint)); + content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint); content.MoveToY(0, hover_transition_time, Easing.OutQuint); base.OnHoverLost(state); @@ -278,30 +285,5 @@ namespace osu.Game.Overlays.Direct Value = value; } } - - private class TransformEdgeEffectRadius : Transform - { - /// - /// Current value of the transformed colour in linear colour space. - /// - private float valueAt(double time) - { - if (time < StartTime) return StartValue; - if (time >= EndTime) return EndValue; - - return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing); - } - - public override string TargetMember => "EdgeEffect.Colour"; - - protected override void Apply(Container c, double time) - { - EdgeEffectParameters e = c.EdgeEffect; - e.Radius = valueAt(time); - c.EdgeEffect = e; - } - - protected override void ReadIntoStartValue(Container d) => StartValue = d.EdgeEffect.Radius; - } } } diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index ee997a2185..046e56573f 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -122,14 +122,14 @@ namespace osu.Game.Overlays.KeyBinding protected override bool OnHover(InputState state) { - this.FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); + FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); return base.OnHover(state); } protected override void OnHoverLost(InputState state) { - this.FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); + FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); base.OnHoverLost(state); }